Merge "fs_mgr_priv: Extend fs wait timeout to 20 seconds"
diff --git a/adb/Android.mk b/adb/Android.mk
index bc8315e..6cd82ec 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -16,7 +16,7 @@
ifeq ($(HOST_OS),linux)
USB_SRCS := usb_linux.c
EXTRA_SRCS := get_my_path_linux.c
- LOCAL_LDLIBS += -lrt -lncurses -lpthread
+ LOCAL_LDLIBS += -lrt -ldl -lpthread
endif
ifeq ($(HOST_OS),darwin)
@@ -77,6 +77,7 @@
LOCAL_CFLAGS += -O2 -g -DADB_HOST=1 -Wall -Wno-unused-parameter
LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE
LOCAL_MODULE := adb
+LOCAL_MODULE_TAGS := debug
LOCAL_STATIC_LIBRARIES := libzipfile libunz libcrypto_static $(EXTRA_STATIC_LIBS)
ifeq ($(USE_SYSDEPS_WIN32),)
@@ -139,7 +140,7 @@
ifneq ($(SDK_ONLY),true)
include $(CLEAR_VARS)
-LOCAL_LDLIBS := -lrt -lncurses -lpthread
+LOCAL_LDLIBS := -lrt -ldl -lpthread
LOCAL_SRC_FILES := \
adb.c \
diff --git a/adb/adb.c b/adb/adb.c
index 4c3364f..cd7f16c 100644
--- a/adb/adb.c
+++ b/adb/adb.c
@@ -25,6 +25,7 @@
#include <string.h>
#include <time.h>
#include <sys/time.h>
+#include <stdint.h>
#include "sysdeps.h"
#include "adb.h"
@@ -34,7 +35,7 @@
#if !ADB_HOST
#include <private/android_filesystem_config.h>
-#include <linux/capability.h>
+#include <sys/capability.h>
#include <linux/prctl.h>
#include <sys/mount.h>
#else
@@ -400,6 +401,10 @@
return "bootloader";
case CS_DEVICE:
return "device";
+ case CS_RECOVERY:
+ return "recovery";
+ case CS_SIDELOAD:
+ return "sideload";
case CS_OFFLINE:
return "offline";
default:
@@ -991,6 +996,7 @@
/* message since the pipe handles must be inheritable, we use a */
/* security attribute */
HANDLE pipe_read, pipe_write;
+ HANDLE stdout_handle, stderr_handle;
SECURITY_ATTRIBUTES sa;
STARTUPINFO startup;
PROCESS_INFORMATION pinfo;
@@ -1010,6 +1016,26 @@
SetHandleInformation( pipe_read, HANDLE_FLAG_INHERIT, 0 );
+ /* Some programs want to launch an adb command and collect its output by
+ * calling CreateProcess with inheritable stdout/stderr handles, then
+ * using read() to get its output. When this happens, the stdout/stderr
+ * handles passed to the adb client process will also be inheritable.
+ * When starting the adb server here, care must be taken to reset them
+ * to non-inheritable.
+ * Otherwise, something bad happens: even if the adb command completes,
+ * the calling process is stuck while read()-ing from the stdout/stderr
+ * descriptors, because they're connected to corresponding handles in the
+ * adb server process (even if the latter never uses/writes to them).
+ */
+ stdout_handle = GetStdHandle( STD_OUTPUT_HANDLE );
+ stderr_handle = GetStdHandle( STD_ERROR_HANDLE );
+ if (stdout_handle != INVALID_HANDLE_VALUE) {
+ SetHandleInformation( stdout_handle, HANDLE_FLAG_INHERIT, 0 );
+ }
+ if (stderr_handle != INVALID_HANDLE_VALUE) {
+ SetHandleInformation( stderr_handle, HANDLE_FLAG_INHERIT, 0 );
+ }
+
ZeroMemory( &startup, sizeof(startup) );
startup.cb = sizeof(startup);
startup.hStdInput = GetStdHandle( STD_INPUT_HANDLE );
@@ -1216,7 +1242,7 @@
/* don't run as root if we are running in secure mode */
if (should_drop_privileges()) {
struct __user_cap_header_struct header;
- struct __user_cap_data_struct cap;
+ struct __user_cap_data_struct cap[2];
if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) != 0) {
exit(1);
@@ -1249,12 +1275,15 @@
exit(1);
}
+ memset(&header, 0, sizeof(header));
+ memset(cap, 0, sizeof(cap));
+
/* set CAP_SYS_BOOT capability, so "adb reboot" will succeed */
- header.version = _LINUX_CAPABILITY_VERSION;
+ header.version = _LINUX_CAPABILITY_VERSION_3;
header.pid = 0;
- cap.effective = cap.permitted = (1 << CAP_SYS_BOOT);
- cap.inheritable = 0;
- capset(&header, &cap);
+ cap[CAP_TO_INDEX(CAP_SYS_BOOT)].effective |= CAP_TO_MASK(CAP_SYS_BOOT);
+ cap[CAP_TO_INDEX(CAP_SYS_BOOT)].permitted |= CAP_TO_MASK(CAP_SYS_BOOT);
+ capset(&header, cap);
D("Local port disabled\n");
} else {
diff --git a/adb/commandline.c b/adb/commandline.c
index cbe4616..a927423 100644
--- a/adb/commandline.c
+++ b/adb/commandline.c
@@ -383,7 +383,7 @@
}
}
-int adb_download_buffer(const char *service, const void* data, int sz,
+int adb_download_buffer(const char *service, const char *fn, const void* data, int sz,
unsigned progress)
{
char buf[4096];
@@ -419,7 +419,7 @@
sz -= xfer;
ptr += xfer;
if(progress) {
- printf("sending: '%s' %4d%% \r", service, (int)(100LL - ((100LL * sz) / (total))));
+ printf("sending: '%s' %4d%% \r", fn, (int)(100LL - ((100LL * sz) / (total))));
fflush(stdout);
}
}
@@ -451,11 +451,11 @@
data = load_file(fn, &sz);
if(data == 0) {
- fprintf(stderr,"* cannot read '%s' *\n", service);
+ fprintf(stderr,"* cannot read '%s' *\n", fn);
return -1;
}
- int status = adb_download_buffer(service, data, sz, progress);
+ int status = adb_download_buffer(service, fn, data, sz, progress);
free(data);
return status;
}
diff --git a/adb/file_sync_client.c b/adb/file_sync_client.c
index 64e393c..354d0fb 100644
--- a/adb/file_sync_client.c
+++ b/adb/file_sync_client.c
@@ -32,7 +32,7 @@
#include "file_sync_service.h"
-static unsigned total_bytes;
+static unsigned long long total_bytes;
static long long start_time;
static long long NOW()
@@ -58,8 +58,8 @@
t = 1000000;
fprintf(stderr,"%lld KB/s (%lld bytes in %lld.%03llds)\n",
- ((((long long) total_bytes) * 1000000LL) / t) / 1024LL,
- (long long) total_bytes, (t / 1000000LL), (t % 1000000LL) / 1000LL);
+ ((total_bytes * 1000000LL) / t) / 1024LL,
+ total_bytes, (t / 1000000LL), (t % 1000000LL) / 1000LL);
}
void sync_quit(int fd)
diff --git a/adb/sysdeps_win32.c b/adb/sysdeps_win32.c
index d41c42c..2105b16 100644
--- a/adb/sysdeps_win32.c
+++ b/adb/sysdeps_win32.c
@@ -781,7 +781,7 @@
void disable_tcp_nagle(int fd)
{
FH fh = _fh_from_int(fd);
- int on;
+ int on = 1;
if ( !fh || fh->clazz != &_fh_socket_class )
return;
diff --git a/adb/usb_vendors.c b/adb/usb_vendors.c
index 1c09b84..e132c67 100644
--- a/adb/usb_vendors.c
+++ b/adb/usb_vendors.c
@@ -131,6 +131,28 @@
#define VENDOR_ID_KOBO 0x2237
// Teleepoch's USB Vendor ID
#define VENDOR_ID_TELEEPOCH 0x2340
+// AnyDATA's USB Vendor ID
+#define VENDOR_ID_ANYDATA 0x16D5
+// Harris's USB Vendor ID
+#define VENDOR_ID_HARRIS 0x19A5
+// OPPO's USB Vendor ID
+#define VENDOR_ID_OPPO 0x22D9
+// Xiaomi's USB Vendor ID
+#define VENDOR_ID_XIAOMI 0x2717
+// BYD's USB Vendor ID
+#define VENDOR_ID_BYD 0x19D1
+// OUYA's USB Vendor ID
+#define VENDOR_ID_OUYA 0x2836
+// Haier's USB Vendor ID
+#define VENDOR_ID_HAIER 0x201E
+// Hisense's USB Vendor ID
+#define VENDOR_ID_HISENSE 0x109b
+// MTK's USB Vendor ID
+#define VENDOR_ID_MTK 0x0e8d
+// B&N Nook's USB Vendor ID
+#define VENDOR_ID_NOOK 0x2080
+// Qisda's USB Vendor ID
+#define VENDOR_ID_QISDA 0x1D45
/** built-in vendor list */
@@ -183,6 +205,17 @@
VENDOR_ID_YULONG_COOLPAD,
VENDOR_ID_KOBO,
VENDOR_ID_TELEEPOCH,
+ VENDOR_ID_ANYDATA,
+ VENDOR_ID_HARRIS,
+ VENDOR_ID_OPPO,
+ VENDOR_ID_XIAOMI,
+ VENDOR_ID_BYD,
+ VENDOR_ID_OUYA,
+ VENDOR_ID_HAIER,
+ VENDOR_ID_HISENSE,
+ VENDOR_ID_MTK,
+ VENDOR_ID_NOOK,
+ VENDOR_ID_QISDA,
};
#define BUILT_IN_VENDOR_COUNT (sizeof(builtInVendorIds)/sizeof(builtInVendorIds[0]))
@@ -235,6 +268,7 @@
break;
}
}
+ fclose(f);
}
}
}
diff --git a/charger/Android.mk b/charger/Android.mk
index fe0c91d..0258604 100644
--- a/charger/Android.mk
+++ b/charger/Android.mk
@@ -12,6 +12,10 @@
LOCAL_CFLAGS := -DCHARGER_DISABLE_INIT_BLANK
endif
+ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
+LOCAL_CFLAGS += -DCHARGER_ENABLE_SUSPEND
+endif
+
LOCAL_MODULE := charger
LOCAL_MODULE_TAGS := optional
LOCAL_FORCE_STATIC_EXECUTABLE := true
@@ -21,6 +25,9 @@
LOCAL_C_INCLUDES := bootable/recovery
LOCAL_STATIC_LIBRARIES := libminui libpixelflinger_static libpng
+ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
+LOCAL_STATIC_LIBRARIES += libsuspend
+endif
LOCAL_STATIC_LIBRARIES += libz libstdc++ libcutils libm libc
include $(BUILD_EXECUTABLE)
diff --git a/charger/charger.c b/charger/charger.c
index c5e4ec2..353bdf0 100644
--- a/charger/charger.c
+++ b/charger/charger.c
@@ -41,6 +41,10 @@
#include <cutils/misc.h>
#include <cutils/uevent.h>
+#ifdef CHARGER_ENABLE_SUSPEND
+#include <suspend/autosuspend.h>
+#endif
+
#include "minui/minui.h"
#ifndef max
@@ -352,6 +356,21 @@
free(supply);
}
+#ifdef CHARGER_ENABLE_SUSPEND
+static int request_suspend(bool enable)
+{
+ if (enable)
+ return autosuspend_enable();
+ else
+ return autosuspend_disable();
+}
+#else
+static int request_suspend(bool enable)
+{
+ return 0;
+}
+#endif
+
static void parse_uevent(const char *msg, struct uevent *uevent)
{
uevent->action = "";
@@ -685,6 +704,8 @@
charger->next_screen_transition = -1;
gr_fb_blank(true);
LOGV("[%lld] animation done\n", now);
+ if (charger->num_supplies_online > 0)
+ request_suspend(true);
return;
}
@@ -824,8 +845,10 @@
}
} else {
/* if the power key got released, force screen state cycle */
- if (key->pending)
+ if (key->pending) {
+ request_suspend(false);
kick_animation(charger->batt_anim);
+ }
}
}
@@ -843,6 +866,7 @@
static void handle_power_supply_state(struct charger *charger, int64_t now)
{
if (charger->num_supplies_online == 0) {
+ request_suspend(false);
if (charger->next_pwr_check == -1) {
charger->next_pwr_check = now + UNPLUGGED_SHUTDOWN_TIME;
LOGI("[%lld] device unplugged: shutting down in %lld (@ %lld)\n",
diff --git a/cpio/mkbootfs.c b/cpio/mkbootfs.c
index 323a09d..3569e27 100644
--- a/cpio/mkbootfs.c
+++ b/cpio/mkbootfs.c
@@ -55,6 +55,7 @@
static void fix_stat(const char *path, struct stat *s)
{
+ uint64_t capabilities;
if (canned_config) {
// Use the list of file uid/gid/modes loaded from the file
// given with -f.
@@ -78,7 +79,7 @@
} else {
// Use the compiled-in fs_config() function.
- fs_config(path, S_ISDIR(s->st_mode), &s->st_uid, &s->st_gid, &s->st_mode);
+ fs_config(path, S_ISDIR(s->st_mode), &s->st_uid, &s->st_gid, &s->st_mode, &capabilities);
}
}
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk
index e48b9af..3fca64f 100644
--- a/debuggerd/Android.mk
+++ b/debuggerd/Android.mk
@@ -37,6 +37,7 @@
LOCAL_MODULE := crasher
LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS += -fstack-protector-all
#LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_SHARED_LIBRARIES := libcutils libc
include $(BUILD_EXECUTABLE)
diff --git a/debuggerd/crasher.c b/debuggerd/crasher.c
index 74eaa49..8c225cb 100644
--- a/debuggerd/crasher.c
+++ b/debuggerd/crasher.c
@@ -17,10 +17,11 @@
#include <cutils/sockets.h>
+extern const char* __progname;
+
void crash1(void);
void crashnostack(void);
-void maybeabort(void);
-int do_action(const char* arg);
+static int do_action(const char* arg);
static void debuggerd_connect()
{
@@ -28,30 +29,44 @@
int s;
sprintf(tmp, "%d", gettid());
s = socket_local_client("android:debuggerd",
- ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
+ ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
if(s >= 0) {
read(s, tmp, 1);
close(s);
}
}
-void test_call1()
+static void maybeabort() {
+ if(time(0) != 42) {
+ abort();
+ }
+}
+
+static int smash_stack(int i) {
+ printf("crasher: deliberately corrupting stack...\n");
+ // Unless there's a "big enough" buffer on the stack, gcc
+ // doesn't bother inserting checks.
+ char buf[8];
+ // If we don't write something relatively unpredicatable
+ // into the buffer and then do something with it, gcc
+ // optimizes everything away and just returns a constant.
+ *(int*)(&buf[7]) = (uintptr_t) &buf[0];
+ return *(int*)(&buf[0]);
+}
+
+__attribute__((noinline)) static void overflow_stack(void* p) {
+ fprintf(stderr, "p = %p\n", p);
+ void* buf[1];
+ buf[0] = p;
+ overflow_stack(&buf);
+}
+
+static void test_call1()
{
*((int*) 32) = 1;
}
-void *test_thread(void *x)
-{
- printf("crasher: thread pid=%d tid=%d\n", getpid(), gettid());
-
- sleep(1);
- test_call1();
- printf("goodbye\n");
-
- return 0;
-}
-
-void *noisy(void *x)
+static void *noisy(void *x)
{
char c = (unsigned) x;
for(;;) {
@@ -62,7 +77,7 @@
return 0;
}
-int ctest()
+static int ctest()
{
pthread_t thr;
pthread_attr_t attr;
@@ -80,7 +95,7 @@
return (void*) do_action((const char*) raw_arg);
}
-int do_action_on_thread(const char* arg)
+static int do_action_on_thread(const char* arg)
{
pthread_t t;
pthread_create(&t, NULL, thread_callback, (void*) arg);
@@ -89,41 +104,75 @@
return (int) result;
}
-int do_action(const char* arg)
+__attribute__((noinline)) static int crash3(int a) {
+ *((int*) 0xdead) = a;
+ return a*4;
+}
+
+__attribute__((noinline)) static int crash2(int a) {
+ a = crash3(a) + 2;
+ return a*3;
+}
+
+__attribute__((noinline)) static int crash(int a) {
+ a = crash2(a) + 1;
+ return a*2;
+}
+
+static void abuse_heap() {
+ char buf[16];
+ free((void*) buf); // GCC is smart enough to warn about this, but we're doing it deliberately.
+}
+
+static int do_action(const char* arg)
{
- if(!strncmp(arg, "thread-", strlen("thread-"))) {
+ fprintf(stderr,"crasher: init pid=%d tid=%d\n", getpid(), gettid());
+
+ if (!strncmp(arg, "thread-", strlen("thread-"))) {
return do_action_on_thread(arg + strlen("thread-"));
+ } else if (!strcmp(arg,"smash-stack")) {
+ return smash_stack(42);
+ } else if (!strcmp(arg,"stack-overflow")) {
+ overflow_stack(NULL);
+ } else if (!strcmp(arg,"nostack")) {
+ crashnostack();
+ } else if (!strcmp(arg,"ctest")) {
+ return ctest();
+ } else if (!strcmp(arg,"exit")) {
+ exit(1);
+ } else if (!strcmp(arg,"crash")) {
+ return crash(42);
+ } else if (!strcmp(arg,"abort")) {
+ maybeabort();
+ } else if (!strcmp(arg, "heap-usage")) {
+ abuse_heap();
}
- if(!strcmp(arg,"nostack")) crashnostack();
- if(!strcmp(arg,"ctest")) return ctest();
- if(!strcmp(arg,"exit")) exit(1);
- if(!strcmp(arg,"abort")) maybeabort();
-
- pthread_t thr;
- pthread_attr_t attr;
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
- pthread_create(&thr, &attr, test_thread, 0);
- while(1) sleep(1);
+ fprintf(stderr, "%s OP\n", __progname);
+ fprintf(stderr, "where OP is:\n");
+ fprintf(stderr, " smash-stack overwrite a stack-guard canary\n");
+ fprintf(stderr, " stack-overflow recurse until the stack overflows\n");
+ fprintf(stderr, " heap-corruption cause a libc abort by corrupting the heap\n");
+ fprintf(stderr, " heap-usage cause a libc abort by abusing a heap function\n");
+ fprintf(stderr, " nostack crash with a NULL stack pointer\n");
+ fprintf(stderr, " ctest (obsoleted by thread-crash?)\n");
+ fprintf(stderr, " exit call exit(1)\n");
+ fprintf(stderr, " crash cause a SIGSEGV\n");
+ fprintf(stderr, " abort call abort()\n");
+ fprintf(stderr, "prefix any of the above with 'thread-' to not run\n");
+ fprintf(stderr, "on the process' main thread.\n");
+ return EXIT_SUCCESS;
}
int main(int argc, char **argv)
{
fprintf(stderr,"crasher: built at " __TIME__ "!@\n");
- fprintf(stderr,"crasher: init pid=%d tid=%d\n", getpid(), gettid());
if(argc > 1) {
return do_action(argv[1]);
} else {
crash1();
-// *((int*) 0) = 42;
}
-
- return 0;
-}
-void maybeabort()
-{
- if(time(0) != 42) abort();
+ return 0;
}
diff --git a/debuggerd/debuggerd.c b/debuggerd/debuggerd.c
index 55222c5..99e6f13 100644
--- a/debuggerd/debuggerd.c
+++ b/debuggerd/debuggerd.c
@@ -52,6 +52,7 @@
debugger_action_t action;
pid_t pid, tid;
uid_t uid, gid;
+ uintptr_t abort_msg_address;
} debugger_request_t;
static int
@@ -207,12 +208,15 @@
}
debugger_msg_t msg;
+ memset(&msg, 0, sizeof(msg));
status = TEMP_FAILURE_RETRY(read(fd, &msg, sizeof(msg)));
if (status < 0) {
LOG("read failure? %s\n", strerror(errno));
return -1;
}
- if (status != sizeof(msg)) {
+ if (status == sizeof(debugger_msg_t)) {
+ XLOG("crash request of size %d abort_msg_address=%#08x\n", status, msg.abort_msg_address);
+ } else {
LOG("invalid crash request of size %d\n", status);
return -1;
}
@@ -222,6 +226,7 @@
out_request->pid = cr.pid;
out_request->uid = cr.uid;
out_request->gid = cr.gid;
+ out_request->abort_msg_address = msg.abort_msg_address;
if (msg.action == DEBUGGER_ACTION_CRASH) {
/* Ensure that the tid reported by the crashing process is valid. */
@@ -265,6 +270,7 @@
XLOG("handle_request(%d)\n", fd);
debugger_request_t request;
+ memset(&request, 0, sizeof(request));
int status = read_request(fd, &request);
if (!status) {
XLOG("BOOM: pid=%d uid=%d gid=%d tid=%d\n",
@@ -308,7 +314,7 @@
if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
XLOG("stopped -- dumping to tombstone\n");
tombstone_path = engrave_tombstone(request.pid, request.tid,
- signal, true, true, &detach_failed,
+ signal, request.abort_msg_address, true, true, &detach_failed,
&total_sleep_time_usec);
} else if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE) {
XLOG("stopped -- dumping to fd\n");
@@ -345,8 +351,8 @@
/* don't dump sibling threads when attaching to GDB because it
* makes the process less reliable, apparently... */
tombstone_path = engrave_tombstone(request.pid, request.tid,
- signal, !attach_gdb, false, &detach_failed,
- &total_sleep_time_usec);
+ signal, request.abort_msg_address, !attach_gdb, false,
+ &detach_failed, &total_sleep_time_usec);
break;
}
diff --git a/debuggerd/tombstone.c b/debuggerd/tombstone.c
index e8b3e24..da5f03c 100644
--- a/debuggerd/tombstone.c
+++ b/debuggerd/tombstone.c
@@ -174,7 +174,7 @@
siginfo_t si;
memset(&si, 0, sizeof(si));
- if(ptrace(PTRACE_GETSIGINFO, tid, 0, &si)){
+ if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si)){
_LOG(log, false, "cannot get siginfo: %s\n", strerror(errno));
} else if (signal_has_address(sig)) {
_LOG(log, false, "signal %d (%s), code %d (%s), fault addr %08x\n",
@@ -616,11 +616,46 @@
dump_log_file(log, pid, "/dev/log/main", tailOnly);
}
+static void dump_abort_message(log_t* log, pid_t tid, uintptr_t address) {
+ if (address == 0) {
+ return;
+ }
+
+ address += sizeof(size_t); // Skip the buffer length.
+
+ char msg[512];
+ memset(msg, 0, sizeof(msg));
+ char* p = &msg[0];
+ while (p < &msg[sizeof(msg)]) {
+ uint32_t data;
+ if (!try_get_word_ptrace(tid, address, &data)) {
+ break;
+ }
+ address += sizeof(uint32_t);
+
+ if ((*p++ = (data >> 0) & 0xff) == 0) {
+ break;
+ }
+ if ((*p++ = (data >> 8) & 0xff) == 0) {
+ break;
+ }
+ if ((*p++ = (data >> 16) & 0xff) == 0) {
+ break;
+ }
+ if ((*p++ = (data >> 24) & 0xff) == 0) {
+ break;
+ }
+ }
+ msg[sizeof(msg) - 1] = '\0';
+
+ _LOG(log, false, "Abort message: '%s'\n", msg);
+}
+
/*
* Dumps all information about the specified pid to the tombstone.
*/
-static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal,
- bool dump_sibling_threads, int* total_sleep_time_usec)
+static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, uintptr_t abort_msg_address,
+ bool dump_sibling_threads, int* total_sleep_time_usec)
{
/* don't copy log messages to tombstone unless this is a dev device */
char value[PROPERTY_VALUE_MAX];
@@ -632,9 +667,10 @@
dump_build_info(log);
dump_revision_info(log);
dump_thread_info(log, pid, tid, true);
- if(signal) {
+ if (signal) {
dump_fault_addr(log, tid, signal);
}
+ dump_abort_message(log, tid, abort_msg_address);
ptrace_context_t* context = load_ptrace_context(tid);
dump_thread(context, log, tid, true, total_sleep_time_usec);
@@ -712,7 +748,7 @@
return strdup(path);
}
-char* engrave_tombstone(pid_t pid, pid_t tid, int signal,
+char* engrave_tombstone(pid_t pid, pid_t tid, int signal, uintptr_t abort_msg_address,
bool dump_sibling_threads, bool quiet, bool* detach_failed,
int* total_sleep_time_usec) {
mkdir(TOMBSTONE_DIR, 0755);
@@ -733,7 +769,7 @@
log_t log;
log.tfd = fd;
log.quiet = quiet;
- *detach_failed = dump_crash(&log, pid, tid, signal, dump_sibling_threads,
+ *detach_failed = dump_crash(&log, pid, tid, signal, abort_msg_address, dump_sibling_threads,
total_sleep_time_usec);
close(fd);
diff --git a/debuggerd/tombstone.h b/debuggerd/tombstone.h
index edcd7b1..d4a1a96 100644
--- a/debuggerd/tombstone.h
+++ b/debuggerd/tombstone.h
@@ -25,7 +25,7 @@
/* Creates a tombstone file and writes the crash dump to it.
* Returns the path of the tombstone, which must be freed using free(). */
-char* engrave_tombstone(pid_t pid, pid_t tid, int signal,
+char* engrave_tombstone(pid_t pid, pid_t tid, int signal, uintptr_t abort_msg_address,
bool dump_sibling_threads, bool quiet, bool* detach_failed, int* total_sleep_time_usec);
#endif // _DEBUGGERD_TOMBSTONE_H
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 5025dae..1189e1f 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -20,6 +20,7 @@
$(LOCAL_PATH)/../../extras/ext4_utils
LOCAL_SRC_FILES := protocol.c engine.c bootimg.c fastboot.c
LOCAL_MODULE := fastboot
+LOCAL_MODULE_TAGS := debug
ifeq ($(HOST_OS),linux)
LOCAL_SRC_FILES += usb_linux.c util_linux.c
diff --git a/fastboot/engine.c b/fastboot/engine.c
index 7a55260..8d46991 100644
--- a/fastboot/engine.c
+++ b/fastboot/engine.c
@@ -29,6 +29,7 @@
#include "fastboot.h"
#include "make_ext4fs.h"
+#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
@@ -45,8 +46,6 @@
#include <sys/mman.h>
#endif
-extern struct fs_info info;
-
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
double now()
@@ -302,10 +301,7 @@
#else
fd = fileno(tmpfile());
#endif
- /* reset ext4fs info so we can be called multiple times */
- reset_ext4fs_info();
- info.len = image->partition_size;
- make_ext4fs_internal(fd, NULL, NULL, NULL, 0, 1, 0, 0, 0, NULL);
+ make_ext4fs_sparse_fd(fd, image->partition_size, NULL, NULL);
fstat(fd, &st);
image->image_size = st.st_size;
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index 2bcc9c4..e51c9cf 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -523,11 +523,11 @@
/* We found our match */
/* First check the filesystem if requested */
if (fstab[i].fs_mgr_flags & MF_WAIT) {
- wait_for_file(fstab[i].blk_dev, WAIT_TIMEOUT);
+ wait_for_file(n_blk_dev, WAIT_TIMEOUT);
}
if (fstab[i].fs_mgr_flags & MF_CHECK) {
- check_fs(fstab[i].blk_dev, fstab[i].type, fstab[i].mnt_point);
+ check_fs(n_blk_dev, fstab[i].type, fstab[i].mnt_point);
}
/* Now mount it where requested */
diff --git a/include/corkscrew/map_info.h b/include/corkscrew/map_info.h
index c9b241d..14bfad6 100644
--- a/include/corkscrew/map_info.h
+++ b/include/corkscrew/map_info.h
@@ -63,6 +63,10 @@
* previous acquired using acquire_my_map_info_list(). */
void release_my_map_info_list(map_info_t* milist);
+/* Flushes the cached memory map so the next call to
+ * acquire_my_map_info_list() gets fresh data. */
+void flush_my_map_info_list();
+
#ifdef __cplusplus
}
#endif
diff --git a/include/cutils/atomic-arm.h b/include/cutils/atomic-arm.h
index 795afd3..172a0cd 100644
--- a/include/cutils/atomic-arm.h
+++ b/include/cutils/atomic-arm.h
@@ -18,55 +18,33 @@
#define ANDROID_CUTILS_ATOMIC_ARM_H
#include <stdint.h>
-#include <machine/cpu-features.h>
#ifndef ANDROID_ATOMIC_INLINE
#define ANDROID_ATOMIC_INLINE inline __attribute__((always_inline))
#endif
-extern ANDROID_ATOMIC_INLINE void android_compiler_barrier(void)
+extern ANDROID_ATOMIC_INLINE void android_compiler_barrier()
{
__asm__ __volatile__ ("" : : : "memory");
}
+extern ANDROID_ATOMIC_INLINE void android_memory_barrier()
+{
#if ANDROID_SMP == 0
-extern ANDROID_ATOMIC_INLINE void android_memory_barrier(void)
-{
android_compiler_barrier();
-}
-extern ANDROID_ATOMIC_INLINE void android_memory_store_barrier(void)
-{
- android_compiler_barrier();
-}
-#elif defined(__ARM_HAVE_DMB)
-extern ANDROID_ATOMIC_INLINE void android_memory_barrier(void)
-{
- __asm__ __volatile__ ("dmb" : : : "memory");
-}
-extern ANDROID_ATOMIC_INLINE void android_memory_store_barrier(void)
-{
- __asm__ __volatile__ ("dmb st" : : : "memory");
-}
-#elif defined(__ARM_HAVE_LDREX_STREX)
-extern ANDROID_ATOMIC_INLINE void android_memory_barrier(void)
-{
- __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 5" : : "r" (0) : "memory");
-}
-extern ANDROID_ATOMIC_INLINE void android_memory_store_barrier(void)
-{
- android_memory_barrier();
-}
#else
-extern ANDROID_ATOMIC_INLINE void android_memory_barrier(void)
-{
- typedef void (kuser_memory_barrier)(void);
- (*(kuser_memory_barrier *)0xffff0fa0)();
-}
-extern ANDROID_ATOMIC_INLINE void android_memory_store_barrier(void)
-{
- android_memory_barrier();
-}
+ __asm__ __volatile__ ("dmb" : : : "memory");
#endif
+}
+
+extern ANDROID_ATOMIC_INLINE void android_memory_store_barrier()
+{
+#if ANDROID_SMP == 0
+ android_compiler_barrier();
+#else
+ __asm__ __volatile__ ("dmb st" : : : "memory");
+#endif
+}
extern ANDROID_ATOMIC_INLINE
int32_t android_atomic_acquire_load(volatile const int32_t *ptr)
@@ -83,32 +61,32 @@
return *ptr;
}
-extern ANDROID_ATOMIC_INLINE void
-android_atomic_acquire_store(int32_t value, volatile int32_t *ptr)
+extern ANDROID_ATOMIC_INLINE
+void android_atomic_acquire_store(int32_t value, volatile int32_t *ptr)
{
*ptr = value;
android_memory_barrier();
}
-extern ANDROID_ATOMIC_INLINE void
-android_atomic_release_store(int32_t value, volatile int32_t *ptr)
+extern ANDROID_ATOMIC_INLINE
+void android_atomic_release_store(int32_t value, volatile int32_t *ptr)
{
android_memory_barrier();
*ptr = value;
}
-#if defined(__thumb__)
-extern int android_atomic_cas(int32_t old_value, int32_t new_value,
- volatile int32_t *ptr);
-#elif defined(__ARM_HAVE_LDREX_STREX)
-extern ANDROID_ATOMIC_INLINE int
-android_atomic_cas(int32_t old_value, int32_t new_value, volatile int32_t *ptr)
+extern ANDROID_ATOMIC_INLINE
+int android_atomic_cas(int32_t old_value, int32_t new_value,
+ volatile int32_t *ptr)
{
int32_t prev, status;
do {
__asm__ __volatile__ ("ldrex %0, [%3]\n"
"mov %1, #0\n"
"teq %0, %4\n"
+#ifdef __thumb2__
+ "it eq\n"
+#endif
"strexeq %1, %5, [%3]"
: "=&r" (prev), "=&r" (status), "+m"(*ptr)
: "r" (ptr), "Ir" (old_value), "r" (new_value)
@@ -116,49 +94,26 @@
} while (__builtin_expect(status != 0, 0));
return prev != old_value;
}
-#else
-extern ANDROID_ATOMIC_INLINE int
-android_atomic_cas(int32_t old_value, int32_t new_value, volatile int32_t *ptr)
-{
- typedef int (kuser_cmpxchg)(int32_t, int32_t, volatile int32_t *);
- int32_t prev, status;
- prev = *ptr;
- do {
- status = (*(kuser_cmpxchg *)0xffff0fc0)(old_value, new_value, ptr);
- if (__builtin_expect(status == 0, 1))
- return 0;
- prev = *ptr;
- } while (prev == old_value);
- return 1;
-}
-#endif
-extern ANDROID_ATOMIC_INLINE int
-android_atomic_acquire_cas(int32_t old_value,
- int32_t new_value,
- volatile int32_t *ptr)
+extern ANDROID_ATOMIC_INLINE
+int android_atomic_acquire_cas(int32_t old_value, int32_t new_value,
+ volatile int32_t *ptr)
{
int status = android_atomic_cas(old_value, new_value, ptr);
android_memory_barrier();
return status;
}
-extern ANDROID_ATOMIC_INLINE int
-android_atomic_release_cas(int32_t old_value,
- int32_t new_value,
- volatile int32_t *ptr)
+extern ANDROID_ATOMIC_INLINE
+int android_atomic_release_cas(int32_t old_value, int32_t new_value,
+ volatile int32_t *ptr)
{
android_memory_barrier();
return android_atomic_cas(old_value, new_value, ptr);
}
-
-#if defined(__thumb__)
-extern int32_t android_atomic_add(int32_t increment,
- volatile int32_t *ptr);
-#elif defined(__ARM_HAVE_LDREX_STREX)
-extern ANDROID_ATOMIC_INLINE int32_t
-android_atomic_add(int32_t increment, volatile int32_t *ptr)
+extern ANDROID_ATOMIC_INLINE
+int32_t android_atomic_add(int32_t increment, volatile int32_t *ptr)
{
int32_t prev, tmp, status;
android_memory_barrier();
@@ -173,19 +128,6 @@
} while (__builtin_expect(status != 0, 0));
return prev;
}
-#else
-extern ANDROID_ATOMIC_INLINE int32_t
-android_atomic_add(int32_t increment, volatile int32_t *ptr)
-{
- int32_t prev, status;
- android_memory_barrier();
- do {
- prev = *ptr;
- status = android_atomic_cas(prev, prev + increment, ptr);
- } while (__builtin_expect(status != 0, 0));
- return prev;
-}
-#endif
extern ANDROID_ATOMIC_INLINE int32_t android_atomic_inc(volatile int32_t *addr)
{
@@ -197,11 +139,8 @@
return android_atomic_add(-1, addr);
}
-#if defined(__thumb__)
-extern int32_t android_atomic_and(int32_t value, volatile int32_t *ptr);
-#elif defined(__ARM_HAVE_LDREX_STREX)
-extern ANDROID_ATOMIC_INLINE int32_t
-android_atomic_and(int32_t value, volatile int32_t *ptr)
+extern ANDROID_ATOMIC_INLINE
+int32_t android_atomic_and(int32_t value, volatile int32_t *ptr)
{
int32_t prev, tmp, status;
android_memory_barrier();
@@ -216,25 +155,9 @@
} while (__builtin_expect(status != 0, 0));
return prev;
}
-#else
-extern ANDROID_ATOMIC_INLINE int32_t
-android_atomic_and(int32_t value, volatile int32_t *ptr)
-{
- int32_t prev, status;
- android_memory_barrier();
- do {
- prev = *ptr;
- status = android_atomic_cas(prev, prev & value, ptr);
- } while (__builtin_expect(status != 0, 0));
- return prev;
-}
-#endif
-#if defined(__thumb__)
-extern int32_t android_atomic_or(int32_t value, volatile int32_t *ptr);
-#elif defined(__ARM_HAVE_LDREX_STREX)
-extern ANDROID_ATOMIC_INLINE int32_t
-android_atomic_or(int32_t value, volatile int32_t *ptr)
+extern ANDROID_ATOMIC_INLINE
+int32_t android_atomic_or(int32_t value, volatile int32_t *ptr)
{
int32_t prev, tmp, status;
android_memory_barrier();
@@ -249,18 +172,5 @@
} while (__builtin_expect(status != 0, 0));
return prev;
}
-#else
-extern ANDROID_ATOMIC_INLINE int32_t
-android_atomic_or(int32_t value, volatile int32_t *ptr)
-{
- int32_t prev, status;
- android_memory_barrier();
- do {
- prev = *ptr;
- status = android_atomic_cas(prev, prev | value, ptr);
- } while (__builtin_expect(status != 0, 0));
- return prev;
-}
-#endif
#endif /* ANDROID_CUTILS_ATOMIC_ARM_H */
diff --git a/include/cutils/debugger.h b/include/cutils/debugger.h
index 5a8e796..4eda523 100644
--- a/include/cutils/debugger.h
+++ b/include/cutils/debugger.h
@@ -34,10 +34,10 @@
DEBUGGER_ACTION_DUMP_BACKTRACE,
} debugger_action_t;
-/* message sent over the socket */
typedef struct {
debugger_action_t action;
pid_t tid;
+ uintptr_t abort_msg_address;
} debugger_msg_t;
/* Dumps a process backtrace, registers, and stack to a tombstone file (requires root).
diff --git a/include/cutils/tztime.h b/include/cutils/tztime.h
index 36ac25d..dbdbd60 100644
--- a/include/cutils/tztime.h
+++ b/include/cutils/tztime.h
@@ -17,45 +17,8 @@
#ifndef _CUTILS_TZTIME_H
#define _CUTILS_TZTIME_H
-#include <time.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-time_t mktime_tz(struct tm * const tmp, char const * tz);
-void localtime_tz(const time_t * const timep, struct tm * tmp, const char* tz);
-
-#ifdef HAVE_ANDROID_OS
-
-/* the following is defined in the Bionic C library on Android, but the
- * declarations are only available through a platform-private header
- */
+// TODO: fix both callers to just include <bionic_time.h> themselves.
#include <bionic_time.h>
-#else /* !HAVE_ANDROID_OS */
-
-struct strftime_locale {
- const char *mon[12]; /* short names */
- const char *month[12]; /* long names */
- const char *standalone_month[12]; /* long standalone names */
- const char *wday[7]; /* short names */
- const char *weekday[7]; /* long names */
- const char *X_fmt;
- const char *x_fmt;
- const char *c_fmt;
- const char *am;
- const char *pm;
- const char *date_fmt;
-};
-
-size_t strftime_tz(char *s, size_t max, const char *format, const struct tm *tm, const struct strftime_locale *locale);
-
-#endif /* !HAVE_ANDROID_OS */
-
-#ifdef __cplusplus
-}
-#endif
-
#endif /* __CUTILS_TZTIME_H */
diff --git a/include/private/android_filesystem_capability.h b/include/private/android_filesystem_capability.h
new file mode 100644
index 0000000..0505cda
--- /dev/null
+++ b/include/private/android_filesystem_capability.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Taken from linux/capability.h, with minor modifications
+ */
+
+#ifndef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_FILESYSTEM_CAPABILITY_H
+#define _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_FILESYSTEM_CAPABILITY_H
+
+#include <stdint.h>
+
+#define __user
+#define __u32 uint32_t
+#define __le32 uint32_t
+
+#define _LINUX_CAPABILITY_VERSION_1 0x19980330
+#define _LINUX_CAPABILITY_U32S_1 1
+#define _LINUX_CAPABILITY_VERSION_2 0x20071026
+#define _LINUX_CAPABILITY_U32S_2 2
+#define _LINUX_CAPABILITY_VERSION_3 0x20080522
+#define _LINUX_CAPABILITY_U32S_3 2
+
+typedef struct __user_cap_header_struct {
+ __u32 version;
+ int pid;
+} __user *cap_user_header_t;
+
+typedef struct __user_cap_data_struct {
+ __u32 effective;
+ __u32 permitted;
+ __u32 inheritable;
+} __user *cap_user_data_t;
+
+#define VFS_CAP_REVISION_MASK 0xFF000000
+#define VFS_CAP_REVISION_SHIFT 24
+#define VFS_CAP_FLAGS_MASK ~VFS_CAP_REVISION_MASK
+#define VFS_CAP_FLAGS_EFFECTIVE 0x000001
+#define VFS_CAP_REVISION_1 0x01000000
+#define VFS_CAP_U32_1 1
+#define XATTR_CAPS_SZ_1 (sizeof(__le32)*(1 + 2*VFS_CAP_U32_1))
+#define VFS_CAP_REVISION_2 0x02000000
+#define VFS_CAP_U32_2 2
+#define XATTR_CAPS_SZ_2 (sizeof(__le32)*(1 + 2*VFS_CAP_U32_2))
+#define XATTR_CAPS_SZ XATTR_CAPS_SZ_2
+#define VFS_CAP_U32 VFS_CAP_U32_2
+#define VFS_CAP_REVISION VFS_CAP_REVISION_2
+
+struct vfs_cap_data {
+ __le32 magic_etc;
+ struct {
+ __le32 permitted;
+ __le32 inheritable;
+ } data[VFS_CAP_U32];
+};
+
+#define _LINUX_CAPABILITY_VERSION _LINUX_CAPABILITY_VERSION_1
+#define _LINUX_CAPABILITY_U32S _LINUX_CAPABILITY_U32S_1
+#define CAP_CHOWN 0
+#define CAP_DAC_OVERRIDE 1
+#define CAP_DAC_READ_SEARCH 2
+#define CAP_FOWNER 3
+#define CAP_FSETID 4
+#define CAP_KILL 5
+#define CAP_SETGID 6
+#define CAP_SETUID 7
+#define CAP_SETPCAP 8
+#define CAP_LINUX_IMMUTABLE 9
+#define CAP_NET_BIND_SERVICE 10
+#define CAP_NET_BROADCAST 11
+#define CAP_NET_ADMIN 12
+#define CAP_NET_RAW 13
+#define CAP_IPC_LOCK 14
+#define CAP_IPC_OWNER 15
+#define CAP_SYS_MODULE 16
+#define CAP_SYS_RAWIO 17
+#define CAP_SYS_CHROOT 18
+#define CAP_SYS_PTRACE 19
+#define CAP_SYS_PACCT 20
+#define CAP_SYS_ADMIN 21
+#define CAP_SYS_BOOT 22
+#define CAP_SYS_NICE 23
+#define CAP_SYS_RESOURCE 24
+#define CAP_SYS_TIME 25
+#define CAP_SYS_TTY_CONFIG 26
+#define CAP_MKNOD 27
+#define CAP_LEASE 28
+#define CAP_AUDIT_WRITE 29
+#define CAP_AUDIT_CONTROL 30
+#define CAP_SETFCAP 31
+#define CAP_MAC_OVERRIDE 32
+#define CAP_MAC_ADMIN 33
+#define CAP_SYSLOG 34
+#define CAP_WAKE_ALARM 35
+#define CAP_LAST_CAP CAP_WAKE_ALARM
+#define cap_valid(x) ((x) >= 0 && (x) <= CAP_LAST_CAP)
+#define CAP_TO_INDEX(x) ((x) >> 5)
+#define CAP_TO_MASK(x) (1 << ((x) & 31))
+
+#undef __user
+#undef __u32
+#undef __le32
+
+#endif
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
index f90af81..53bd166 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.h
@@ -25,6 +25,13 @@
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <stdint.h>
+
+#ifdef HAVE_ANDROID_OS
+#include <linux/capability.h>
+#else
+#include "android_filesystem_capability.h"
+#endif
/* This is the master Users and Groups config for the platform.
** DO NOT EVER RENUMBER.
@@ -63,6 +70,7 @@
#define AID_NFC 1027 /* nfc subsystem */
#define AID_SDCARD_R 1028 /* external storage read access */
#define AID_CLAT 1029 /* clat part of nat464 */
+#define AID_LOOP_RADIO 1030 /* loop radio devices */
#define AID_SHELL 2000 /* adb and debug shell user */
#define AID_CACHE 2001 /* cache access */
@@ -138,6 +146,7 @@
{ "net_admin", AID_NET_ADMIN, },
{ "net_bw_stats", AID_NET_BW_STATS, },
{ "net_bw_acct", AID_NET_BW_ACCT, },
+ { "loop_radio", AID_LOOP_RADIO, },
{ "misc", AID_MISC, },
{ "nobody", AID_NOBODY, },
{ "clat", AID_CLAT, },
@@ -150,6 +159,7 @@
unsigned mode;
unsigned uid;
unsigned gid;
+ uint64_t capabilities;
const char *prefix;
};
@@ -159,26 +169,26 @@
** way up to the root.
*/
-static struct fs_path_config android_dirs[] = {
- { 00770, AID_SYSTEM, AID_CACHE, "cache" },
- { 00771, AID_SYSTEM, AID_SYSTEM, "data/app" },
- { 00771, AID_SYSTEM, AID_SYSTEM, "data/app-private" },
- { 00771, AID_SYSTEM, AID_SYSTEM, "data/dalvik-cache" },
- { 00771, AID_SYSTEM, AID_SYSTEM, "data/data" },
- { 00771, AID_SHELL, AID_SHELL, "data/local/tmp" },
- { 00771, AID_SHELL, AID_SHELL, "data/local" },
- { 01771, AID_SYSTEM, AID_MISC, "data/misc" },
- { 00770, AID_DHCP, AID_DHCP, "data/misc/dhcp" },
- { 00775, AID_MEDIA_RW, AID_MEDIA_RW, "data/media" },
- { 00775, AID_MEDIA_RW, AID_MEDIA_RW, "data/media/Music" },
- { 00771, AID_SYSTEM, AID_SYSTEM, "data" },
- { 00750, AID_ROOT, AID_SHELL, "sbin" },
- { 00755, AID_ROOT, AID_SHELL, "system/bin" },
- { 00755, AID_ROOT, AID_SHELL, "system/vendor" },
- { 00755, AID_ROOT, AID_SHELL, "system/xbin" },
- { 00755, AID_ROOT, AID_ROOT, "system/etc/ppp" },
- { 00777, AID_ROOT, AID_ROOT, "sdcard" },
- { 00755, AID_ROOT, AID_ROOT, 0 },
+static const struct fs_path_config android_dirs[] = {
+ { 00770, AID_SYSTEM, AID_CACHE, 0, "cache" },
+ { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app" },
+ { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private" },
+ { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/dalvik-cache" },
+ { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/data" },
+ { 00771, AID_SHELL, AID_SHELL, 0, "data/local/tmp" },
+ { 00771, AID_SHELL, AID_SHELL, 0, "data/local" },
+ { 01771, AID_SYSTEM, AID_MISC, 0, "data/misc" },
+ { 00770, AID_DHCP, AID_DHCP, 0, "data/misc/dhcp" },
+ { 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media" },
+ { 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/Music" },
+ { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data" },
+ { 00750, AID_ROOT, AID_SHELL, 0, "sbin" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "system/bin" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "system/vendor" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "system/xbin" },
+ { 00755, AID_ROOT, AID_ROOT, 0, "system/etc/ppp" },
+ { 00777, AID_ROOT, AID_ROOT, 0, "sdcard" },
+ { 00755, AID_ROOT, AID_ROOT, 0, 0 },
};
/* Rules for files.
@@ -187,53 +197,56 @@
** way up to the root. Prefixes ending in * denotes wildcard
** and will allow partial matches.
*/
-static struct fs_path_config android_files[] = {
- { 00440, AID_ROOT, AID_SHELL, "system/etc/init.goldfish.rc" },
- { 00550, AID_ROOT, AID_SHELL, "system/etc/init.goldfish.sh" },
- { 00440, AID_ROOT, AID_SHELL, "system/etc/init.trout.rc" },
- { 00550, AID_ROOT, AID_SHELL, "system/etc/init.ril" },
- { 00550, AID_ROOT, AID_SHELL, "system/etc/init.testmenu" },
- { 00550, AID_DHCP, AID_SHELL, "system/etc/dhcpcd/dhcpcd-run-hooks" },
- { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/dbus.conf" },
- { 00444, AID_RADIO, AID_AUDIO, "system/etc/AudioPara4.csv" },
- { 00555, AID_ROOT, AID_ROOT, "system/etc/ppp/*" },
- { 00555, AID_ROOT, AID_ROOT, "system/etc/rc.*" },
- { 00644, AID_SYSTEM, AID_SYSTEM, "data/app/*" },
- { 00644, AID_MEDIA_RW, AID_MEDIA_RW, "data/media/*" },
- { 00644, AID_SYSTEM, AID_SYSTEM, "data/app-private/*" },
- { 00644, AID_APP, AID_APP, "data/data/*" },
- /* the following two files are INTENTIONALLY set-gid and not set-uid.
- * Do not change. */
- { 02755, AID_ROOT, AID_NET_RAW, "system/bin/ping" },
- { 02750, AID_ROOT, AID_INET, "system/bin/netcfg" },
- /* the following five files are INTENTIONALLY set-uid, but they
- * are NOT included on user builds. */
- { 06755, AID_ROOT, AID_ROOT, "system/xbin/su" },
- { 06755, AID_ROOT, AID_ROOT, "system/xbin/librank" },
- { 06755, AID_ROOT, AID_ROOT, "system/xbin/procrank" },
- { 06755, AID_ROOT, AID_ROOT, "system/xbin/procmem" },
- { 06755, AID_ROOT, AID_ROOT, "system/xbin/tcpdump" },
- { 04770, AID_ROOT, AID_RADIO, "system/bin/pppd-ril" },
- /* the following file is INTENTIONALLY set-uid, and IS included
- * in user builds. */
- { 06750, AID_ROOT, AID_SHELL, "system/bin/run-as" },
- { 00755, AID_ROOT, AID_SHELL, "system/bin/*" },
- { 00755, AID_ROOT, AID_ROOT, "system/lib/valgrind/*" },
- { 00755, AID_ROOT, AID_SHELL, "system/xbin/*" },
- { 00755, AID_ROOT, AID_SHELL, "system/vendor/bin/*" },
- { 00750, AID_ROOT, AID_SHELL, "sbin/*" },
- { 00755, AID_ROOT, AID_ROOT, "bin/*" },
- { 00750, AID_ROOT, AID_SHELL, "init*" },
- { 00750, AID_ROOT, AID_SHELL, "charger*" },
- { 00750, AID_ROOT, AID_SHELL, "sbin/fs_mgr" },
- { 00640, AID_ROOT, AID_SHELL, "fstab.*" },
- { 00644, AID_ROOT, AID_ROOT, 0 },
+static const struct fs_path_config android_files[] = {
+ { 00440, AID_ROOT, AID_SHELL, 0, "system/etc/init.goldfish.rc" },
+ { 00550, AID_ROOT, AID_SHELL, 0, "system/etc/init.goldfish.sh" },
+ { 00440, AID_ROOT, AID_SHELL, 0, "system/etc/init.trout.rc" },
+ { 00550, AID_ROOT, AID_SHELL, 0, "system/etc/init.ril" },
+ { 00550, AID_ROOT, AID_SHELL, 0, "system/etc/init.testmenu" },
+ { 00550, AID_DHCP, AID_SHELL, 0, "system/etc/dhcpcd/dhcpcd-run-hooks" },
+ { 00440, AID_BLUETOOTH, AID_BLUETOOTH, 0, "system/etc/dbus.conf" },
+ { 00444, AID_RADIO, AID_AUDIO, 0, "system/etc/AudioPara4.csv" },
+ { 00555, AID_ROOT, AID_ROOT, 0, "system/etc/ppp/*" },
+ { 00555, AID_ROOT, AID_ROOT, 0, "system/etc/rc.*" },
+ { 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app/*" },
+ { 00644, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/*" },
+ { 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private/*" },
+ { 00644, AID_APP, AID_APP, 0, "data/data/*" },
+ { 00755, AID_ROOT, AID_ROOT, 0, "system/bin/ping" },
+
+ /* the following file is INTENTIONALLY set-gid and not set-uid.
+ * Do not change. */
+ { 02750, AID_ROOT, AID_INET, 0, "system/bin/netcfg" },
+
+ /* the following five files are INTENTIONALLY set-uid, but they
+ * are NOT included on user builds. */
+ { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/su" },
+ { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/librank" },
+ { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/procrank" },
+ { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/procmem" },
+ { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/tcpdump" },
+ { 04770, AID_ROOT, AID_RADIO, 0, "system/bin/pppd-ril" },
+
+ /* the following file has enhanced capabilities and IS included in user builds. */
+ { 00750, AID_ROOT, AID_SHELL, (1 << CAP_SETUID) | (1 << CAP_SETGID), "system/bin/run-as" },
+
+ { 00755, AID_ROOT, AID_SHELL, 0, "system/bin/*" },
+ { 00755, AID_ROOT, AID_ROOT, 0, "system/lib/valgrind/*" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "system/xbin/*" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "system/vendor/bin/*" },
+ { 00750, AID_ROOT, AID_SHELL, 0, "sbin/*" },
+ { 00755, AID_ROOT, AID_ROOT, 0, "bin/*" },
+ { 00750, AID_ROOT, AID_SHELL, 0, "init*" },
+ { 00750, AID_ROOT, AID_SHELL, 0, "charger*" },
+ { 00750, AID_ROOT, AID_SHELL, 0, "sbin/fs_mgr" },
+ { 00640, AID_ROOT, AID_SHELL, 0, "fstab.*" },
+ { 00644, AID_ROOT, AID_ROOT, 0, 0 },
};
static inline void fs_config(const char *path, int dir,
- unsigned *uid, unsigned *gid, unsigned *mode)
+ unsigned *uid, unsigned *gid, unsigned *mode, uint64_t *capabilities)
{
- struct fs_path_config *pc;
+ const struct fs_path_config *pc;
int plen;
if (path[0] == '/') {
@@ -259,6 +272,7 @@
*uid = pc->uid;
*gid = pc->gid;
*mode = (*mode & (~07777)) | pc->mode;
+ *capabilities = pc->capabilities;
#if 0
fprintf(stderr,"< '%s' '%s' %d %d %o >\n",
diff --git a/init/builtins.c b/init/builtins.c
index dc7900e..07bd6d3 100644
--- a/init/builtins.c
+++ b/init/builtins.c
@@ -590,8 +590,7 @@
struct service *svc;
svc = service_find_by_name(args[1]);
if (svc) {
- service_stop(svc);
- service_start(svc, NULL);
+ service_restart(svc);
}
return 0;
}
diff --git a/init/devices.c b/init/devices.c
index dd875d6..69f5fc8 100644
--- a/init/devices.c
+++ b/init/devices.c
@@ -83,7 +83,8 @@
struct platform_node {
char *name;
- int name_len;
+ char *path;
+ int path_len;
struct listnode list;
};
@@ -126,6 +127,7 @@
char buf[512];
struct listnode *node;
struct perms_ *dp;
+ char *secontext;
/* upaths omit the "/sys" that paths in this list
* contain, so we add 4 when comparing...
@@ -147,6 +149,14 @@
INFO("fixup %s %d %d 0%o\n", buf, dp->uid, dp->gid, dp->perm);
chown(buf, dp->uid, dp->gid);
chmod(buf, dp->perm);
+ if (sehandle) {
+ secontext = NULL;
+ selabel_lookup(sehandle, &secontext, buf, 0);
+ if (secontext) {
+ setfilecon(buf, secontext);
+ freecon(secontext);
+ }
+ }
}
}
@@ -215,61 +225,69 @@
}
}
-static void add_platform_device(const char *name)
+static void add_platform_device(const char *path)
{
- int name_len = strlen(name);
+ int path_len = strlen(path);
struct listnode *node;
struct platform_node *bus;
+ const char *name = path;
+
+ if (!strncmp(path, "/devices/", 9)) {
+ name += 9;
+ if (!strncmp(name, "platform/", 9))
+ name += 9;
+ }
list_for_each_reverse(node, &platform_names) {
bus = node_to_item(node, struct platform_node, list);
- if ((bus->name_len < name_len) &&
- (name[bus->name_len] == '/') &&
- !strncmp(name, bus->name, bus->name_len))
+ if ((bus->path_len < path_len) &&
+ (path[bus->path_len] == '/') &&
+ !strncmp(path, bus->path, bus->path_len))
/* subdevice of an existing platform, ignore it */
return;
}
- INFO("adding platform device %s\n", name);
+ INFO("adding platform device %s (%s)\n", name, path);
bus = calloc(1, sizeof(struct platform_node));
- bus->name = strdup(name);
- bus->name_len = name_len;
+ bus->path = strdup(path);
+ bus->path_len = path_len;
+ bus->name = bus->path + (name - path);
list_add_tail(&platform_names, &bus->list);
}
/*
- * given a name that may start with a platform device, find the length of the
+ * given a path that may start with a platform device, find the length of the
* platform device prefix. If it doesn't start with a platform device, return
* 0.
*/
-static const char *find_platform_device(const char *name)
+static struct platform_node *find_platform_device(const char *path)
{
- int name_len = strlen(name);
+ int path_len = strlen(path);
struct listnode *node;
struct platform_node *bus;
list_for_each_reverse(node, &platform_names) {
bus = node_to_item(node, struct platform_node, list);
- if ((bus->name_len < name_len) &&
- (name[bus->name_len] == '/') &&
- !strncmp(name, bus->name, bus->name_len))
- return bus->name;
+ if ((bus->path_len < path_len) &&
+ (path[bus->path_len] == '/') &&
+ !strncmp(path, bus->path, bus->path_len))
+ return bus;
}
return NULL;
}
-static void remove_platform_device(const char *name)
+static void remove_platform_device(const char *path)
{
struct listnode *node;
struct platform_node *bus;
list_for_each_reverse(node, &platform_names) {
bus = node_to_item(node, struct platform_node, list);
- if (!strcmp(name, bus->name)) {
- INFO("removing platform device %s\n", name);
- free(bus->name);
+ if (!strcmp(path, bus->path)) {
+ INFO("removing platform device %s\n", bus->name);
+ free(bus->path);
list_remove(node);
free(bus);
return;
@@ -355,8 +373,10 @@
char **links;
int link_num = 0;
int width;
+ struct platform_node *pdev;
- if (strncmp(uevent->path, "/devices/platform/", 18))
+ pdev = find_platform_device(uevent->path);
+ if (!pdev)
return NULL;
links = malloc(sizeof(char *) * 2);
@@ -365,7 +385,7 @@
memset(links, 0, sizeof(char *) * 2);
/* skip "/devices/platform/<driver>" */
- parent = strchr(uevent->path + 18, '/');
+ parent = strchr(uevent->path + pdev->path_len, '/');
if (!*parent)
goto err;
@@ -402,7 +422,7 @@
static char **parse_platform_block_device(struct uevent *uevent)
{
const char *device;
- const char *path;
+ struct platform_node *pdev;
char *slash;
int width;
char buf[256];
@@ -414,18 +434,16 @@
unsigned int size;
struct stat info;
+ pdev = find_platform_device(uevent->path);
+ if (!pdev)
+ return NULL;
+ device = pdev->name;
+
char **links = malloc(sizeof(char *) * 4);
if (!links)
return NULL;
memset(links, 0, sizeof(char *) * 4);
- /* Drop "/devices/platform/" */
- path = uevent->path;
- device = path + 18;
- device = find_platform_device(device);
- if (!device)
- goto err;
-
INFO("found platform device %s\n", device);
snprintf(link_path, sizeof(link_path), "/dev/block/platform/%s", device);
@@ -447,17 +465,13 @@
links[link_num] = NULL;
}
- slash = strrchr(path, '/');
+ slash = strrchr(uevent->path, '/');
if (asprintf(&links[link_num], "%s/%s", link_path, slash + 1) > 0)
link_num++;
else
links[link_num] = NULL;
return links;
-
-err:
- free(links);
- return NULL;
}
static void handle_device(const char *action, const char *devpath,
@@ -490,12 +504,12 @@
static void handle_platform_device_event(struct uevent *uevent)
{
- const char *name = uevent->path + 18; /* length of /devices/platform/ */
+ const char *path = uevent->path;
if (!strcmp(uevent->action, "add"))
- add_platform_device(name);
+ add_platform_device(path);
else if (!strcmp(uevent->action, "remove"))
- remove_platform_device(name);
+ remove_platform_device(path);
}
static const char *parse_device_name(struct uevent *uevent, unsigned int len)
@@ -533,7 +547,7 @@
snprintf(devpath, sizeof(devpath), "%s%s", base, name);
make_dir(base, 0755);
- if (!strncmp(uevent->path, "/devices/platform/", 18))
+ if (!strncmp(uevent->path, "/devices/", 9))
links = parse_platform_block_device(uevent);
handle_device(uevent->action, devpath, uevent->path, 1,
@@ -881,8 +895,8 @@
sehandle = selinux_android_file_context_handle();
}
- /* is 64K enough? udev uses 16MB! */
- device_fd = uevent_open_socket(64*1024, true);
+ /* is 256K enough? udev uses 16MB! */
+ device_fd = uevent_open_socket(256*1024, true);
if(device_fd < 0)
return;
diff --git a/init/init.c b/init/init.c
index b20b434..c21a495 100755
--- a/init/init.c
+++ b/init/init.c
@@ -31,7 +31,6 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
-#include <sys/personality.h>
#include <selinux/selinux.h>
#include <selinux/label.h>
@@ -91,7 +90,7 @@
}
static int have_console;
-static char *console_name = "/dev/console";
+static char console_name[PROP_VALUE_MAX] = "/dev/console";
static time_t process_needs_restart;
static const char *ENV[32];
@@ -165,7 +164,7 @@
* state and immediately takes it out of the restarting
* state if it was in there
*/
- svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET));
+ svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART));
svc->time_started = 0;
/* running processes require no additional work -- if
@@ -198,28 +197,36 @@
}
if (is_selinux_enabled() > 0) {
- char *mycon = NULL, *fcon = NULL;
+ if (svc->seclabel) {
+ scon = strdup(svc->seclabel);
+ if (!scon) {
+ ERROR("Out of memory while starting '%s'\n", svc->name);
+ return;
+ }
+ } else {
+ char *mycon = NULL, *fcon = NULL;
- INFO("computing context for service '%s'\n", svc->args[0]);
- rc = getcon(&mycon);
- if (rc < 0) {
- ERROR("could not get context while starting '%s'\n", svc->name);
- return;
- }
+ INFO("computing context for service '%s'\n", svc->args[0]);
+ rc = getcon(&mycon);
+ if (rc < 0) {
+ ERROR("could not get context while starting '%s'\n", svc->name);
+ return;
+ }
- rc = getfilecon(svc->args[0], &fcon);
- if (rc < 0) {
- ERROR("could not get context while starting '%s'\n", svc->name);
+ rc = getfilecon(svc->args[0], &fcon);
+ if (rc < 0) {
+ ERROR("could not get context while starting '%s'\n", svc->name);
+ freecon(mycon);
+ return;
+ }
+
+ rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon);
freecon(mycon);
- return;
- }
-
- rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon);
- freecon(mycon);
- freecon(fcon);
- if (rc < 0) {
- ERROR("could not get context while starting '%s'\n", svc->name);
- return;
+ freecon(fcon);
+ if (rc < 0) {
+ ERROR("could not get context while starting '%s'\n", svc->name);
+ return;
+ }
}
}
@@ -234,21 +241,6 @@
int fd, sz;
umask(077);
-#ifdef __arm__
- /*
- * b/7188322 - Temporarily revert to the compat memory layout
- * to avoid breaking third party apps.
- *
- * THIS WILL GO AWAY IN A FUTURE ANDROID RELEASE.
- *
- * http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=7dbaa466
- * changes the kernel mapping from bottom up to top-down.
- * This breaks some programs which improperly embed
- * an out of date copy of Android's linker.
- */
- int current = personality(0xffffFFFF);
- personality(current | ADDR_COMPAT_LAYOUT);
-#endif
if (properties_inited()) {
get_property_workspace(&fd, &sz);
sprintf(tmp, "%d,%d", dup(fd), sz);
@@ -367,15 +359,14 @@
notify_service_state(svc->name, "running");
}
-/* The how field should be either SVC_DISABLED or SVC_RESET */
+/* The how field should be either SVC_DISABLED, SVC_RESET, or SVC_RESTART */
static void service_stop_or_reset(struct service *svc, int how)
{
- /* we are no longer running, nor should we
- * attempt to restart
- */
- svc->flags &= (~(SVC_RUNNING|SVC_RESTARTING));
+ /* The service is still SVC_RUNNING until its process exits, but if it has
+ * already exited it shoudn't attempt a restart yet. */
+ svc->flags &= (~SVC_RESTARTING);
- if ((how != SVC_DISABLED) && (how != SVC_RESET)) {
+ if ((how != SVC_DISABLED) && (how != SVC_RESET) && (how != SVC_RESTART)) {
/* Hrm, an illegal flag. Default to SVC_DISABLED */
how = SVC_DISABLED;
}
@@ -407,6 +398,17 @@
service_stop_or_reset(svc, SVC_DISABLED);
}
+void service_restart(struct service *svc)
+{
+ if (svc->flags & SVC_RUNNING) {
+ /* Stop, wait, then start the service. */
+ service_stop_or_reset(svc, SVC_RESTART);
+ } else if (!(svc->flags & SVC_RESTARTING)) {
+ /* Just start the service since it's not running. */
+ service_start(svc, NULL);
+ } /* else: Service is restarting anyways. */
+}
+
void property_changed(const char *name, const char *value)
{
if (property_triggers_enabled)
@@ -438,7 +440,7 @@
static void msg_start(const char *name)
{
- struct service *svc;
+ struct service *svc = NULL;
char *tmp = NULL;
char *args = NULL;
@@ -446,11 +448,13 @@
svc = service_find_by_name(name);
else {
tmp = strdup(name);
- args = strchr(tmp, ':');
- *args = '\0';
- args++;
+ if (tmp) {
+ args = strchr(tmp, ':');
+ *args = '\0';
+ args++;
- svc = service_find_by_name(tmp);
+ svc = service_find_by_name(tmp);
+ }
}
if (svc) {
@@ -473,6 +477,17 @@
}
}
+static void msg_restart(const char *name)
+{
+ struct service *svc = service_find_by_name(name);
+
+ if (svc) {
+ service_restart(svc);
+ } else {
+ ERROR("no such service '%s'\n", name);
+ }
+}
+
void handle_control_message(const char *msg, const char *arg)
{
if (!strcmp(msg,"start")) {
@@ -480,8 +495,7 @@
} else if (!strcmp(msg,"stop")) {
msg_stop(arg);
} else if (!strcmp(msg,"restart")) {
- msg_stop(arg);
- msg_start(arg);
+ msg_restart(arg);
} else {
ERROR("unknown control msg '%s'\n", msg);
}
@@ -555,11 +569,9 @@
static int console_init_action(int nargs, char **args)
{
int fd;
- char tmp[PROP_VALUE_MAX];
if (console[0]) {
- snprintf(tmp, sizeof(tmp), "/dev/%s", console);
- console_name = strdup(tmp);
+ snprintf(console_name, sizeof(console_name), "/dev/%s", console);
}
fd = open(console_name, O_RDWR);
@@ -633,7 +645,7 @@
static void export_kernel_boot_props(void)
{
char tmp[PROP_VALUE_MAX];
- const char *pval;
+ int ret;
unsigned i;
struct {
const char *src_prop;
@@ -647,22 +659,26 @@
};
for (i = 0; i < ARRAY_SIZE(prop_map); i++) {
- pval = property_get(prop_map[i].src_prop);
- property_set(prop_map[i].dest_prop, pval ?: prop_map[i].def_val);
+ ret = property_get(prop_map[i].src_prop, tmp);
+ if (ret > 0)
+ property_set(prop_map[i].dest_prop, tmp);
+ else
+ property_set(prop_map[i].dest_prop, prop_map[i].def_val);
}
- pval = property_get("ro.boot.console");
- if (pval)
- strlcpy(console, pval, sizeof(console));
+ ret = property_get("ro.boot.console", tmp);
+ if (ret)
+ strlcpy(console, tmp, sizeof(console));
/* save a copy for init's usage during boot */
- strlcpy(bootmode, property_get("ro.bootmode"), sizeof(bootmode));
+ property_get("ro.bootmode", tmp);
+ strlcpy(bootmode, tmp, sizeof(bootmode));
/* if this was given on kernel command line, override what we read
* before (e.g. from /proc/cpuinfo), if anything */
- pval = property_get("ro.boot.hardware");
- if (pval)
- strlcpy(hardware, pval, sizeof(hardware));
+ ret = property_get("ro.boot.hardware", tmp);
+ if (ret)
+ strlcpy(hardware, tmp, sizeof(hardware));
property_set("ro.hardware", hardware);
snprintf(tmp, PROP_VALUE_MAX, "%d", revision);
@@ -753,7 +769,7 @@
#endif
static const struct selinux_opt seopts_prop[] = {
- { SELABEL_OPT_PATH, "/data/system/property_contexts" },
+ { SELABEL_OPT_PATH, "/data/security/property_contexts" },
{ SELABEL_OPT_PATH, "/property_contexts" },
{ 0, NULL }
};
diff --git a/init/init.h b/init/init.h
index 955e1f0..aa6a4ab 100644
--- a/init/init.h
+++ b/init/init.h
@@ -72,6 +72,7 @@
#define SVC_RESET 0x40 /* Use when stopping a process, but not disabling
so it can be restarted with its class */
#define SVC_RC_DISABLED 0x80 /* Remember if the disabled flag was set in the rc script */
+#define SVC_RESTART 0x100 /* Use to safely restart (stop, wait, start) a service */
#define NR_SVC_SUPP_GIDS 12 /* twelve supplementary groups */
@@ -127,6 +128,7 @@
void (*func)(struct service *svc));
void service_stop(struct service *svc);
void service_reset(struct service *svc);
+void service_restart(struct service *svc);
void service_start(struct service *svc, const char *dynamic_args);
void property_changed(const char *name, const char *value);
diff --git a/init/init_parser.c b/init/init_parser.c
index beb9188..cce1093 100644
--- a/init/init_parser.c
+++ b/init/init_parser.c
@@ -206,8 +206,9 @@
while (*src_ptr && left > 0) {
char *c;
char prop[PROP_NAME_MAX + 1];
- const char *prop_val;
+ char prop_val[PROP_VALUE_MAX];
int prop_len = 0;
+ int prop_val_len;
c = strchr(src_ptr, '$');
if (!c) {
@@ -265,14 +266,14 @@
goto err;
}
- prop_val = property_get(prop);
- if (!prop_val) {
+ prop_val_len = property_get(prop, prop_val);
+ if (!prop_val_len) {
ERROR("property '%s' doesn't exist while expanding '%s'\n",
prop, src);
goto err;
}
- ret = push_chars(&dst_ptr, &left, prop_val, strlen(prop_val));
+ ret = push_chars(&dst_ptr, &left, prop_val, prop_val_len);
if (ret < 0)
goto err_nospace;
src_ptr = c;
@@ -543,7 +544,7 @@
const char* equals = strchr(name, '=');
if (equals) {
char prop_name[PROP_NAME_MAX + 1];
- const char* value;
+ char value[PROP_VALUE_MAX];
int length = equals - name;
if (length > PROP_NAME_MAX) {
ERROR("property name too long in trigger %s", act->name);
@@ -552,9 +553,8 @@
prop_name[length] = 0;
/* does the property exist, and match the trigger value? */
- value = property_get(prop_name);
- if (value && (!strcmp(equals + 1, value) ||
- !strcmp(equals + 1, "*"))) {
+ property_get(prop_name, value);
+ if (!strcmp(equals + 1, value) ||!strcmp(equals + 1, "*")) {
action_add_queue_tail(act);
}
}
@@ -571,6 +571,7 @@
act = calloc(1, sizeof(*act));
act->name = name;
list_init(&act->commands);
+ list_init(&act->qlist);
cmd = calloc(1, sizeof(*cmd));
cmd->func = func;
@@ -583,7 +584,9 @@
void action_add_queue_tail(struct action *act)
{
- list_add_tail(&action_queue, &act->qlist);
+ if (list_empty(&act->qlist)) {
+ list_add_tail(&action_queue, &act->qlist);
+ }
}
struct action *action_remove_queue_head(void)
@@ -594,6 +597,7 @@
struct listnode *node = list_head(&action_queue);
struct action *act = node_to_item(node, struct action, qlist);
list_remove(node);
+ list_init(node);
return act;
}
}
@@ -825,6 +829,7 @@
act = calloc(1, sizeof(*act));
act->name = args[1];
list_init(&act->commands);
+ list_init(&act->qlist);
list_add_tail(&action_list, &act->alist);
/* XXX add to hash */
return act;
diff --git a/init/keychords.c b/init/keychords.c
index aab0819..d18a6e4 100644
--- a/init/keychords.c
+++ b/init/keychords.c
@@ -95,24 +95,23 @@
void handle_keychord()
{
struct service *svc;
- const char* debuggable;
- const char* adb_enabled;
+ char debuggable[PROP_VALUE_MAX];
+ char adb_enabled[PROP_VALUE_MAX];
int ret;
__u16 id;
// only handle keychords if ro.debuggable is set or adb is enabled.
// the logic here is that bugreports should be enabled in userdebug or eng builds
// and on user builds for users that are developers.
- debuggable = property_get("ro.debuggable");
- adb_enabled = property_get("init.svc.adbd");
+ property_get("ro.debuggable", debuggable);
+ property_get("init.svc.adbd", adb_enabled);
ret = read(keychord_fd, &id, sizeof(id));
if (ret != sizeof(id)) {
ERROR("could not read keychord id\n");
return;
}
- if ((debuggable && !strcmp(debuggable, "1")) ||
- (adb_enabled && !strcmp(adb_enabled, "running"))) {
+ if (!strcmp(debuggable, "1") || !strcmp(adb_enabled, "running")) {
svc = service_find_by_keychord(id);
if (svc) {
INFO("starting service %s from keychord\n", svc->name);
diff --git a/init/property_service.c b/init/property_service.c
index 61dd86f..70ee4a6 100755
--- a/init/property_service.c
+++ b/init/property_service.c
@@ -110,7 +110,6 @@
};
typedef struct {
- void *data;
size_t size;
int fd;
} workspace;
@@ -118,85 +117,34 @@
static int init_workspace(workspace *w, size_t size)
{
void *data;
- int fd;
-
- /* dev is a tmpfs that we can use to carve a shared workspace
- * out of, so let's do that...
- */
- fd = open("/dev/__properties__", O_RDWR | O_CREAT | O_NOFOLLOW, 0600);
+ int fd = open(PROP_FILENAME, O_RDONLY | O_NOFOLLOW);
if (fd < 0)
return -1;
- if (ftruncate(fd, size) < 0)
- goto out;
-
- data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- if(data == MAP_FAILED)
- goto out;
-
- close(fd);
-
- fd = open("/dev/__properties__", O_RDONLY | O_NOFOLLOW);
- if (fd < 0)
- return -1;
-
- unlink("/dev/__properties__");
-
- w->data = data;
w->size = size;
w->fd = fd;
return 0;
-
-out:
- close(fd);
- return -1;
}
-/* (8 header words + 247 toc words) = 1020 bytes */
-/* 1024 bytes header and toc + 247 prop_infos @ 128 bytes = 32640 bytes */
-
-#define PA_COUNT_MAX 247
-#define PA_INFO_START 1024
-#define PA_SIZE 32768
-
static workspace pa_workspace;
-static prop_info *pa_info_array;
-
-extern prop_area *__system_property_area__;
static int init_property_area(void)
{
- prop_area *pa;
-
- if(pa_info_array)
+ if (property_area_inited)
return -1;
- if(init_workspace(&pa_workspace, PA_SIZE))
+ if(__system_property_area_init())
+ return -1;
+
+ if(init_workspace(&pa_workspace, 0))
return -1;
fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC);
- pa_info_array = (void*) (((char*) pa_workspace.data) + PA_INFO_START);
-
- pa = pa_workspace.data;
- memset(pa, 0, PA_SIZE);
- pa->magic = PROP_AREA_MAGIC;
- pa->version = PROP_AREA_VERSION;
-
- /* plug into the lib property services */
- __system_property_area__ = pa;
property_area_inited = 1;
return 0;
}
-static void update_prop_info(prop_info *pi, const char *value, unsigned len)
-{
- pi->serial = pi->serial | 1;
- memcpy(pi->value, value, len + 1);
- pi->serial = (len << 24) | ((pi->serial + 1) & 0xffffff);
- __futex_wake(&pi->serial, INT32_MAX);
-}
-
static int check_mac_perms(const char *name, char *sctx)
{
if (is_selinux_enabled() <= 0)
@@ -292,19 +240,9 @@
return 0;
}
-const char* property_get(const char *name)
+int __property_get(const char *name, char *value)
{
- prop_info *pi;
-
- if(strlen(name) >= PROP_NAME_MAX) return 0;
-
- pi = (prop_info*) __system_property_find(name);
-
- if(pi != 0) {
- return pi->value;
- } else {
- return 0;
- }
+ return __system_property_get(name, value);
}
static void write_persistent_property(const char *name, const char *value)
@@ -331,8 +269,8 @@
int property_set(const char *name, const char *value)
{
- prop_area *pa;
prop_info *pi;
+ int ret;
size_t namelen = strlen(name);
size_t valuelen = strlen(value);
@@ -347,25 +285,13 @@
/* ro.* properties may NEVER be modified once set */
if(!strncmp(name, "ro.", 3)) return -1;
- pa = __system_property_area__;
- update_prop_info(pi, value, valuelen);
- pa->serial++;
- __futex_wake(&pa->serial, INT32_MAX);
+ __system_property_update(pi, value, valuelen);
} else {
- pa = __system_property_area__;
- if(pa->count == PA_COUNT_MAX) return -1;
-
- pi = pa_info_array + pa->count;
- pi->serial = (valuelen << 24);
- memcpy(pi->name, name, namelen + 1);
- memcpy(pi->value, value, valuelen + 1);
-
- pa->toc[pa->count] =
- (namelen << 24) | (((unsigned) pi) - ((unsigned) pa));
-
- pa->count++;
- pa->serial++;
- __futex_wake(&pa->serial, INT32_MAX);
+ ret = __system_property_add(name, namelen, value, valuelen);
+ if (ret < 0) {
+ ERROR("Failed to set '%s'='%s'", name, value);
+ return ret;
+ }
}
/* If name starts with "net." treat as a DNS property. */
if (strncmp("net.", name, strlen("net.")) == 0) {
@@ -587,8 +513,11 @@
static void load_override_properties() {
#ifdef ALLOW_LOCAL_PROP_OVERRIDE
- const char *debuggable = property_get("ro.debuggable");
- if (debuggable && (strcmp(debuggable, "1") == 0)) {
+ char debuggable[PROP_VALUE_MAX];
+ int ret;
+
+ ret = property_get("ro.debuggable", debuggable);
+ if (ret && (strcmp(debuggable, "1") == 0)) {
load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE);
}
#endif /* ALLOW_LOCAL_PROP_OVERRIDE */
diff --git a/init/property_service.h b/init/property_service.h
index b9d1bf6..46cbd8f 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -18,6 +18,7 @@
#define _INIT_PROPERTY_H
#include <stdbool.h>
+#include <sys/system_properties.h>
extern void handle_property_set_fd(void);
extern void property_init(void);
@@ -25,9 +26,25 @@
extern void load_persist_props(void);
extern void start_property_service(void);
void get_property_workspace(int *fd, int *sz);
-extern const char* property_get(const char *name);
+extern int __property_get(const char *name, char *value);
extern int property_set(const char *name, const char *value);
extern int properties_inited();
int get_property_set_fd(void);
+extern void __property_get_size_error()
+ __attribute__((__error__("property_get called with too small buffer")));
+
+static inline
+__attribute__ ((always_inline))
+__attribute__ ((gnu_inline))
+__attribute__ ((artificial))
+int property_get(const char *name, char *value)
+{
+ size_t value_len = __builtin_object_size(value, 0);
+ if (value_len != PROP_VALUE_MAX)
+ __property_get_size_error();
+
+ return __property_get(name, value);
+}
+
#endif /* _INIT_PROPERTY_H */
diff --git a/init/signal_handler.c b/init/signal_handler.c
index abccb40..d31ad63 100644
--- a/init/signal_handler.c
+++ b/init/signal_handler.c
@@ -63,7 +63,7 @@
NOTICE("process '%s', pid %d exited\n", svc->name, pid);
- if (!(svc->flags & SVC_ONESHOT)) {
+ if (!(svc->flags & SVC_ONESHOT) || (svc->flags & SVC_RESTART)) {
kill(-pid, SIGKILL);
NOTICE("process '%s' killing any children in process group\n", svc->name);
}
@@ -78,8 +78,9 @@
svc->pid = 0;
svc->flags &= (~SVC_RUNNING);
- /* oneshot processes go into the disabled state on exit */
- if (svc->flags & SVC_ONESHOT) {
+ /* oneshot processes go into the disabled state on exit,
+ * except when manually restarted. */
+ if ((svc->flags & SVC_ONESHOT) && !(svc->flags & SVC_RESTART)) {
svc->flags |= SVC_DISABLED;
}
@@ -90,7 +91,7 @@
}
now = gettime();
- if (svc->flags & SVC_CRITICAL) {
+ if ((svc->flags & SVC_CRITICAL) && !(svc->flags & SVC_RESTART)) {
if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) {
if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) {
ERROR("critical process '%s' exited %d times in %d minutes; "
@@ -105,6 +106,7 @@
}
}
+ svc->flags &= (~SVC_RESTART);
svc->flags |= SVC_RESTARTING;
/* Execute all onrestart commands for this service. */
diff --git a/libcorkscrew/Android.mk b/libcorkscrew/Android.mk
index 2786d8f..25512e2 100644
--- a/libcorkscrew/Android.mk
+++ b/libcorkscrew/Android.mk
@@ -59,23 +59,28 @@
# Build test.
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := test.c
-LOCAL_CFLAGS += -std=gnu99 -Werror -fno-inline-small-functions
+LOCAL_SRC_FILES := test.cpp
+LOCAL_CFLAGS += -Werror -fno-inline-small-functions
LOCAL_SHARED_LIBRARIES := libcorkscrew
LOCAL_MODULE := libcorkscrew_test
LOCAL_MODULE_TAGS := optional
include $(BUILD_EXECUTABLE)
+# TODO: reenable darwin-x86
+# ifeq ($(HOST_ARCH),x86)
ifeq ($(HOST_OS)-$(HOST_ARCH),linux-x86)
# Build libcorkscrew.
include $(CLEAR_VARS)
LOCAL_SRC_FILES += $(generic_src_files) $(x86_src_files)
LOCAL_CFLAGS += -DCORKSCREW_HAVE_ARCH
-LOCAL_SHARED_LIBRARIES += libgccdemangle
LOCAL_STATIC_LIBRARIES += libcutils
-LOCAL_LDLIBS += -ldl -lrt
+LOCAL_LDLIBS += -ldl
+ifeq ($(HOST_OS),linux)
+ LOCAL_SHARED_LIBRARIES += libgccdemangle # TODO: is this even needed on Linux?
+ LOCAL_LDLIBS += -lrt
+endif
LOCAL_CFLAGS += -std=gnu99 -Werror
LOCAL_MODULE := libcorkscrew
LOCAL_MODULE_TAGS := optional
@@ -83,11 +88,11 @@
# Build test.
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := test.c
-LOCAL_CFLAGS += -std=gnu99 -Werror -fno-inline-small-functions
+LOCAL_SRC_FILES := test.cpp
+LOCAL_CFLAGS += -Werror
LOCAL_SHARED_LIBRARIES := libcorkscrew
LOCAL_MODULE := libcorkscrew_test
LOCAL_MODULE_TAGS := optional
include $(BUILD_HOST_EXECUTABLE)
-endif # linux-x86
+endif # HOST_ARCH == x86
diff --git a/libcorkscrew/arch-mips/backtrace-mips.c b/libcorkscrew/arch-mips/backtrace-mips.c
index 07d4a24..1abdd83 100644
--- a/libcorkscrew/arch-mips/backtrace-mips.c
+++ b/libcorkscrew/arch-mips/backtrace-mips.c
@@ -90,7 +90,8 @@
if (frame)
frame->stack_top = state->sp;
- ALOGV("#%d: frame=%p pc=%08x sp=%08x\n", index, frame, frame->absolute_pc, frame->stack_top);
+ ALOGV("#%d: frame=%p pc=%08x sp=%08x\n",
+ index, frame, frame->absolute_pc, frame->stack_top);
for (addr = state->pc; maxcheck-- > 0 && !found_start; addr -= 4) {
uint32_t op;
@@ -115,7 +116,7 @@
ALOGV("@0x%08x: found ra offset=%d\n", addr, ra_offset);
break;
case 0x3c1c0000: // lui gp
- ALOGV("@0x%08x: found function boundary\n", addr);
+ ALOGV("@0x%08x: found function boundary\n", addr);
found_start = true;
break;
default:
@@ -162,7 +163,8 @@
state.pc = uc->pc;
state.ra = uc->ra;
- ALOGV("unwind_backtrace_signal_arch: ignore_depth=%d max_depth=%d pc=0x%08x sp=0x%08x ra=0x%08x\n",
+ ALOGV("unwind_backtrace_signal_arch: "
+ "ignore_depth=%d max_depth=%d pc=0x%08x sp=0x%08x ra=0x%08x\n",
ignore_depth, max_depth, state.pc, state.sp, state.ra);
memory_t memory;
@@ -184,7 +186,8 @@
state.ra = regs.regs[31];
state.pc = regs.epc;
- ALOGV("unwind_backtrace_ptrace_arch: ignore_depth=%d max_depth=%d pc=0x%08x sp=0x%08x ra=0x%08x\n",
+ ALOGV("unwind_backtrace_ptrace_arch: "
+ "ignore_depth=%d max_depth=%d pc=0x%08x sp=0x%08x ra=0x%08x\n",
ignore_depth, max_depth, state.pc, state.sp, state.ra);
memory_t memory;
diff --git a/libcorkscrew/arch-x86/backtrace-x86.c b/libcorkscrew/arch-x86/backtrace-x86.c
old mode 100644
new mode 100755
index fb79a0c..e133ab6
--- a/libcorkscrew/arch-x86/backtrace-x86.c
+++ b/libcorkscrew/arch-x86/backtrace-x86.c
@@ -23,13 +23,16 @@
#include "../backtrace-arch.h"
#include "../backtrace-helper.h"
+#include "../ptrace-arch.h"
#include <corkscrew/ptrace.h>
+#include "dwarf.h"
#include <stdlib.h>
#include <signal.h>
#include <stdbool.h>
#include <limits.h>
#include <errno.h>
+#include <string.h>
#include <sys/ptrace.h>
#include <cutils/log.h>
@@ -72,53 +75,746 @@
#endif /* __BIONIC_HAVE_UCONTEXT_T */
-#else /* __BIONIC__ */
+#elif defined(__APPLE__)
+
+#define _XOPEN_SOURCE
+#include <ucontext.h>
+
+#else
// glibc has its own renaming of the Linux kernel's structures.
#define __USE_GNU // For REG_EBP, REG_ESP, and REG_EIP.
#include <ucontext.h>
-#endif /* __ BIONIC__ */
+#endif
/* Unwind state. */
typedef struct {
- uint32_t ebp;
- uint32_t eip;
- uint32_t esp;
+ uint32_t reg[DWARF_REGISTERS];
} unwind_state_t;
+typedef struct {
+ backtrace_frame_t* backtrace;
+ size_t ignore_depth;
+ size_t max_depth;
+ size_t ignored_frames;
+ size_t returned_frames;
+ memory_t memory;
+} backtrace_state_t;
+
uintptr_t rewind_pc_arch(const memory_t* memory __attribute__((unused)), uintptr_t pc) {
- // TODO: Implement for x86.
- return pc;
+ /* TODO: x86 instructions are 1-16 bytes, to define exact size of previous instruction
+ we have to disassemble from the function entry point up to pc.
+ Returning pc-1 is probably enough for now, the only drawback is that
+ it points somewhere between the first byte of instruction we are looking for and
+ the first byte of the next instruction. */
+
+ return pc-1;
+ /* TODO: We should adjust that for the signal frames and return pc for them instead of pc-1.
+ To recognize signal frames we should read cie_info property. */
}
-static ssize_t unwind_backtrace_common(const memory_t* memory,
- const map_info_t* map_info_list __attribute__((unused)),
- unwind_state_t* state, backtrace_frame_t* backtrace,
- size_t ignore_depth, size_t max_depth) {
- size_t ignored_frames = 0;
- size_t returned_frames = 0;
+/* Read byte through 4 byte cache. Usually we read byte by byte and updating cursor. */
+static bool try_get_byte(const memory_t* memory, uintptr_t ptr, uint8_t* out_value, uint32_t* cursor) {
+ static uintptr_t lastptr;
+ static uint32_t buf;
- for (size_t index = 0; state->ebp && returned_frames < max_depth; index++) {
- backtrace_frame_t* frame = add_backtrace_entry(
- index ? rewind_pc_arch(memory, state->eip) : state->eip,
- backtrace, ignore_depth, max_depth,
- &ignored_frames, &returned_frames);
- uint32_t next_esp = state->ebp + 8;
- if (frame) {
- frame->stack_top = state->esp;
- if (state->esp < next_esp) {
- frame->stack_size = next_esp - state->esp;
- }
+ ptr += *cursor;
+
+ if (ptr < lastptr || lastptr + 3 < ptr) {
+ lastptr = (ptr >> 2) << 2;
+ if (!try_get_word(memory, lastptr, &buf)) {
+ return false;
}
- state->esp = next_esp;
- if (!try_get_word(memory, state->ebp + 4, &state->eip)
- || !try_get_word(memory, state->ebp, &state->ebp)
- || !state->eip) {
+ }
+ *out_value = (uint8_t)((buf >> ((ptr & 3) * 8)) & 0xff);
+ ++*cursor;
+ return true;
+}
+
+/* Getting X bytes. 4 is maximum for now. */
+static bool try_get_xbytes(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint8_t bytes, uint32_t* cursor) {
+ uint32_t data = 0;
+ if (bytes > 4) {
+ ALOGE("can't read more than 4 bytes, trying to read %d", bytes);
+ return false;
+ }
+ for (int i = 0; i < bytes; i++) {
+ uint8_t buf;
+ if (!try_get_byte(memory, ptr, &buf, cursor)) {
+ return false;
+ }
+ data |= (uint32_t)buf << (i * 8);
+ }
+ *out_value = data;
+ return true;
+}
+
+/* Reads signed/unsigned LEB128 encoded data. From 1 to 4 bytes. */
+static bool try_get_leb128(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint32_t* cursor, bool sign_extend) {
+ uint8_t buf = 0;
+ uint32_t val = 0;
+ uint8_t c = 0;
+ do {
+ if (!try_get_byte(memory, ptr, &buf, cursor)) {
+ return false;
+ }
+ val |= ((uint32_t)buf & 0x7f) << (c * 7);
+ c++;
+ } while (buf & 0x80 && (c * 7) <= 32);
+ if (c * 7 > 32) {
+ ALOGE("%s: data exceeds expected 4 bytes maximum", __FUNCTION__);
+ return false;
+ }
+ if (sign_extend) {
+ if (buf & 0x40) {
+ val |= ((uint32_t)-1 << (c * 7));
+ }
+ }
+ *out_value = val;
+ return true;
+}
+
+/* Reads signed LEB128 encoded data. From 1 to 4 bytes. */
+static bool try_get_sleb128(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint32_t* cursor) {
+ return try_get_leb128(memory, ptr, out_value, cursor, true);
+}
+
+/* Reads unsigned LEB128 encoded data. From 1 to 4 bytes. */
+static bool try_get_uleb128(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint32_t* cursor) {
+ return try_get_leb128(memory, ptr, out_value, cursor, false);
+}
+
+/* Getting data encoded by dwarf encodings. */
+static bool read_dwarf(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint8_t encoding, uint32_t* cursor) {
+ uint32_t data = 0;
+ bool issigned = true;
+ uintptr_t addr = ptr + *cursor;
+ /* Lower 4 bits is data type/size */
+ /* TODO: add more encodings if it becomes necessary */
+ switch (encoding & 0xf) {
+ case DW_EH_PE_absptr:
+ if (!try_get_xbytes(memory, ptr, &data, 4, cursor)) {
+ return false;
+ }
+ *out_value = data;
+ return true;
+ case DW_EH_PE_udata4:
+ issigned = false;
+ case DW_EH_PE_sdata4:
+ if (!try_get_xbytes(memory, ptr, &data, 4, cursor)) {
+ return false;
+ }
break;
+ default:
+ ALOGE("unrecognized dwarf lower part encoding: 0x%x", encoding);
+ return false;
+ }
+ /* Higher 4 bits is modifier */
+ /* TODO: add more encodings if it becomes necessary */
+ switch (encoding & 0xf0) {
+ case 0:
+ *out_value = data;
+ break;
+ case DW_EH_PE_pcrel:
+ if (issigned) {
+ *out_value = addr + (int32_t)data;
+ } else {
+ *out_value = addr + data;
+ }
+ break;
+ /* Assuming ptr is correct base to calculate datarel */
+ case DW_EH_PE_datarel:
+ if (issigned) {
+ *out_value = ptr + (int32_t)data;
+ } else {
+ *out_value = ptr + data;
+ }
+ break;
+ default:
+ ALOGE("unrecognized dwarf higher part encoding: 0x%x", encoding);
+ return false;
+ }
+ return true;
+}
+
+/* Having PC find corresponding FDE by reading .eh_frame_hdr section data. */
+static uintptr_t find_fde(const memory_t* memory,
+ const map_info_t* map_info_list, uintptr_t pc) {
+ if (!pc) {
+ ALOGV("find_fde: pc is zero, no eh_frame");
+ return 0;
+ }
+ const map_info_t* mi = find_map_info(map_info_list, pc);
+ if (!mi) {
+ ALOGV("find_fde: no map info for pc:0x%x", pc);
+ return 0;
+ }
+ const map_info_data_t* midata = mi->data;
+ if (!midata) {
+ ALOGV("find_fde: no eh_frame_hdr for map: start=0x%x, end=0x%x", mi->start, mi->end);
+ return 0;
+ }
+
+ eh_frame_hdr_info_t eh_hdr_info;
+ memset(&eh_hdr_info, 0, sizeof(eh_frame_hdr_info_t));
+
+ /* Getting the first word of eh_frame_hdr:
+ 1st byte is version;
+ 2nd byte is encoding of pointer to eh_frames;
+ 3rd byte is encoding of count of FDEs in lookup table;
+ 4th byte is encoding of lookup table entries.
+ */
+ uintptr_t eh_frame_hdr = midata->eh_frame_hdr;
+ uint32_t c = 0;
+ if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.version, &c)) return 0;
+ if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.eh_frame_ptr_enc, &c)) return 0;
+ if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.fde_count_enc, &c)) return 0;
+ if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.fde_table_enc, &c)) return 0;
+
+ /* TODO: 3rd byte can be DW_EH_PE_omit, that means no lookup table available and we should
+ try to parse eh_frame instead. Not sure how often it may occur, skipping now.
+ */
+ if (eh_hdr_info.version != 1) {
+ ALOGV("find_fde: eh_frame_hdr version %d is not supported", eh_hdr_info.version);
+ return 0;
+ }
+ /* Getting the data:
+ 2nd word is eh_frame pointer (normally not used, because lookup table has all we need);
+ 3rd word is count of FDEs in the lookup table;
+ starting from 4 word there is FDE lookup table (pairs of PC and FDE pointer) sorted by PC;
+ */
+ if (!read_dwarf(memory, eh_frame_hdr, &eh_hdr_info.eh_frame_ptr, eh_hdr_info.eh_frame_ptr_enc, &c)) return 0;
+ if (!read_dwarf(memory, eh_frame_hdr, &eh_hdr_info.fde_count, eh_hdr_info.fde_count_enc, &c)) return 0;
+ ALOGV("find_fde: found %d FDEs", eh_hdr_info.fde_count);
+
+ int32_t low = 0;
+ int32_t high = eh_hdr_info.fde_count;
+ uintptr_t start = 0;
+ uintptr_t fde = 0;
+ /* eh_frame_hdr + c points to lookup table at this point. */
+ while (low <= high) {
+ uint32_t mid = (high + low)/2;
+ uint32_t entry = c + mid * 8;
+ if (!read_dwarf(memory, eh_frame_hdr, &start, eh_hdr_info.fde_table_enc, &entry)) return 0;
+ if (pc <= start) {
+ high = mid - 1;
+ } else {
+ low = mid + 1;
+ }
+ }
+ /* Value found is at high. */
+ if (high < 0) {
+ ALOGV("find_fde: pc %x is out of FDE bounds: %x", pc, start);
+ return 0;
+ }
+ c += high * 8;
+ if (!read_dwarf(memory, eh_frame_hdr, &start, eh_hdr_info.fde_table_enc, &c)) return 0;
+ if (!read_dwarf(memory, eh_frame_hdr, &fde, eh_hdr_info.fde_table_enc, &c)) return 0;
+ ALOGV("pc 0x%x, ENTRY %d: start=0x%x, fde=0x%x", pc, high, start, fde);
+ return fde;
+}
+
+/* Execute single dwarf instruction and update dwarf state accordingly. */
+static bool execute_dwarf(const memory_t* memory, uintptr_t ptr, cie_info_t* cie_info,
+ dwarf_state_t* dstate, uint32_t* cursor,
+ dwarf_state_t* stack, uint8_t* stack_ptr) {
+ uint8_t inst;
+ uint8_t op = 0;
+
+ if (!try_get_byte(memory, ptr, &inst, cursor)) {
+ return false;
+ }
+ ALOGV("DW_CFA inst: 0x%x", inst);
+
+ /* For some instructions upper 2 bits is opcode and lower 6 bits is operand. See dwarf-2.0 7.23. */
+ if (inst & 0xc0) {
+ op = inst & 0x3f;
+ inst &= 0xc0;
+ }
+
+ switch ((dwarf_CFA)inst) {
+ uint32_t reg = 0;
+ uint32_t offset = 0;
+ case DW_CFA_advance_loc:
+ dstate->loc += op * cie_info->code_align;
+ ALOGV("DW_CFA_advance_loc: %d to 0x%x", op, dstate->loc);
+ break;
+ case DW_CFA_offset:
+ if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false;
+ dstate->regs[op].rule = 'o';
+ dstate->regs[op].value = offset * cie_info->data_align;
+ ALOGV("DW_CFA_offset: r%d = o(%d)", op, dstate->regs[op].value);
+ break;
+ case DW_CFA_restore:
+ dstate->regs[op].rule = stack->regs[op].rule;
+ dstate->regs[op].value = stack->regs[op].value;
+ ALOGV("DW_CFA_restore: r%d = %c(%d)", op, dstate->regs[op].rule, dstate->regs[op].value);
+ break;
+ case DW_CFA_nop:
+ break;
+ case DW_CFA_set_loc: // probably we don't have it on x86.
+ if (!try_get_xbytes(memory, ptr, &offset, 4, cursor)) return false;
+ if (offset < dstate->loc) {
+ ALOGE("DW_CFA_set_loc: attempt to move location backward");
+ return false;
+ }
+ dstate->loc = offset * cie_info->code_align;
+ ALOGV("DW_CFA_set_loc: %d to 0x%x", offset * cie_info->code_align, dstate->loc);
+ break;
+ case DW_CFA_advance_loc1:
+ if (!try_get_byte(memory, ptr, (uint8_t*)&offset, cursor)) return false;
+ dstate->loc += (uint8_t)offset * cie_info->code_align;
+ ALOGV("DW_CFA_advance_loc1: %d to 0x%x", (uint8_t)offset * cie_info->code_align, dstate->loc);
+ break;
+ case DW_CFA_advance_loc2:
+ if (!try_get_xbytes(memory, ptr, &offset, 2, cursor)) return false;
+ dstate->loc += (uint16_t)offset * cie_info->code_align;
+ ALOGV("DW_CFA_advance_loc2: %d to 0x%x", (uint16_t)offset * cie_info->code_align, dstate->loc);
+ break;
+ case DW_CFA_advance_loc4:
+ if (!try_get_xbytes(memory, ptr, &offset, 4, cursor)) return false;
+ dstate->loc += offset * cie_info->code_align;
+ ALOGV("DW_CFA_advance_loc4: %d to 0x%x", offset * cie_info->code_align, dstate->loc);
+ break;
+ case DW_CFA_offset_extended: // probably we don't have it on x86.
+ if (!try_get_uleb128(memory, ptr, ®, cursor)) return false;
+ if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false;
+ if (reg > DWARF_REGISTERS) {
+ ALOGE("DW_CFA_offset_extended: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS);
+ return false;
+ }
+ dstate->regs[reg].rule = 'o';
+ dstate->regs[reg].value = offset * cie_info->data_align;
+ ALOGV("DW_CFA_offset_extended: r%d = o(%d)", reg, dstate->regs[reg].value);
+ break;
+ case DW_CFA_restore_extended: // probably we don't have it on x86.
+ if (!try_get_uleb128(memory, ptr, ®, cursor)) return false;
+ dstate->regs[reg].rule = stack->regs[reg].rule;
+ dstate->regs[reg].value = stack->regs[reg].value;
+ if (reg > DWARF_REGISTERS) {
+ ALOGE("DW_CFA_restore_extended: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS);
+ return false;
+ }
+ ALOGV("DW_CFA_restore: r%d = %c(%d)", reg, dstate->regs[reg].rule, dstate->regs[reg].value);
+ break;
+ case DW_CFA_undefined: // probably we don't have it on x86.
+ if (!try_get_uleb128(memory, ptr, ®, cursor)) return false;
+ dstate->regs[reg].rule = 'u';
+ dstate->regs[reg].value = 0;
+ if (reg > DWARF_REGISTERS) {
+ ALOGE("DW_CFA_undefined: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS);
+ return false;
+ }
+ ALOGV("DW_CFA_undefined: r%d", reg);
+ break;
+ case DW_CFA_same_value: // probably we don't have it on x86.
+ if (!try_get_uleb128(memory, ptr, ®, cursor)) return false;
+ dstate->regs[reg].rule = 's';
+ dstate->regs[reg].value = 0;
+ if (reg > DWARF_REGISTERS) {
+ ALOGE("DW_CFA_undefined: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS);
+ return false;
+ }
+ ALOGV("DW_CFA_same_value: r%d", reg);
+ break;
+ case DW_CFA_register: // probably we don't have it on x86.
+ if (!try_get_uleb128(memory, ptr, ®, cursor)) return false;
+ /* that's new register actually, not offset */
+ if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false;
+ if (reg > DWARF_REGISTERS || offset > DWARF_REGISTERS) {
+ ALOGE("DW_CFA_register: r%d or r%d exceeds supported number of registers (%d)", reg, offset, DWARF_REGISTERS);
+ return false;
+ }
+ dstate->regs[reg].rule = 'r';
+ dstate->regs[reg].value = offset;
+ ALOGV("DW_CFA_register: r%d = r(%d)", reg, dstate->regs[reg].value);
+ break;
+ case DW_CFA_remember_state:
+ if (*stack_ptr == DWARF_STATES_STACK) {
+ ALOGE("DW_CFA_remember_state: states stack overflow %d", *stack_ptr);
+ return false;
+ }
+ stack[(*stack_ptr)++] = *dstate;
+ ALOGV("DW_CFA_remember_state: stacktop moves to %d", *stack_ptr);
+ break;
+ case DW_CFA_restore_state:
+ /* We have CIE state saved at 0 position. It's not supposed to be taken
+ by DW_CFA_restore_state. */
+ if (*stack_ptr == 1) {
+ ALOGE("DW_CFA_restore_state: states stack is empty");
+ return false;
+ }
+ /* Don't touch location on restore. */
+ uintptr_t saveloc = dstate->loc;
+ *dstate = stack[--*stack_ptr];
+ dstate->loc = saveloc;
+ ALOGV("DW_CFA_restore_state: stacktop moves to %d", *stack_ptr);
+ break;
+ case DW_CFA_def_cfa:
+ if (!try_get_uleb128(memory, ptr, ®, cursor)) return false;
+ if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false;
+ dstate->cfa_reg = reg;
+ dstate->cfa_off = offset;
+ ALOGV("DW_CFA_def_cfa: %x(r%d)", offset, reg);
+ break;
+ case DW_CFA_def_cfa_register:
+ if (!try_get_uleb128(memory, ptr, ®, cursor)) {
+ return false;
+ }
+ dstate->cfa_reg = reg;
+ ALOGV("DW_CFA_def_cfa_register: r%d", reg);
+ break;
+ case DW_CFA_def_cfa_offset:
+ if (!try_get_uleb128(memory, ptr, &offset, cursor)) {
+ return false;
+ }
+ dstate->cfa_off = offset;
+ ALOGV("DW_CFA_def_cfa_offset: %x", offset);
+ break;
+ default:
+ ALOGE("unrecognized DW_CFA_* instruction: 0x%x", inst);
+ return false;
+ }
+ return true;
+}
+
+/* Restoring particular register value based on dwarf state. */
+static bool get_old_register_value(const memory_t* memory, uint32_t cfa,
+ dwarf_state_t* dstate, uint8_t reg,
+ unwind_state_t* state, unwind_state_t* newstate) {
+ uint32_t addr;
+ switch (dstate->regs[reg].rule) {
+ case 0:
+ /* We don't have dstate updated for this register, so assuming value kept the same.
+ Normally we should look into state and return current value as the old one
+ but we don't have all registers in state to handle this properly */
+ ALOGV("get_old_register_value: value of r%d is the same", reg);
+ // for ESP if it's not updated by dwarf rule we assume it's equal to CFA
+ if (reg == DWARF_ESP) {
+ ALOGV("get_old_register_value: adjusting esp to CFA: 0x%x", cfa);
+ newstate->reg[reg] = cfa;
+ } else {
+ newstate->reg[reg] = state->reg[reg];
+ }
+ break;
+ case 'o':
+ addr = cfa + (int32_t)dstate->regs[reg].value;
+ if (!try_get_word(memory, addr, &newstate->reg[reg])) {
+ ALOGE("get_old_register_value: can't read from 0x%x", addr);
+ return false;
+ }
+ ALOGV("get_old_register_value: r%d at 0x%x is 0x%x", reg, addr, newstate->reg[reg]);
+ break;
+ case 'r':
+ /* We don't have all registers in state so don't even try to look at 'r' */
+ ALOGE("get_old_register_value: register lookup not implemented yet");
+ break;
+ default:
+ ALOGE("get_old_register_value: unexpected rule:%c value:%d for register %d",
+ dstate->regs[reg].rule, (int32_t)dstate->regs[reg].value, reg);
+ return false;
+ }
+ return true;
+}
+
+/* Updaing state based on dwarf state. */
+static bool update_state(const memory_t* memory, unwind_state_t* state,
+ dwarf_state_t* dstate, cie_info_t* cie_info) {
+ unwind_state_t newstate;
+ /* We can restore more registers here if we need them. Meanwile doing minimal work here. */
+ /* Getting CFA. */
+ uintptr_t cfa = 0;
+ if (dstate->cfa_reg == DWARF_ESP) {
+ cfa = state->reg[DWARF_ESP] + dstate->cfa_off;
+ } else if (dstate->cfa_reg == DWARF_EBP) {
+ cfa = state->reg[DWARF_EBP] + dstate->cfa_off;
+ } else {
+ ALOGE("update_state: unexpected CFA register: %d", dstate->cfa_reg);
+ return false;
+ }
+ ALOGV("update_state: new CFA: 0x%x", cfa);
+ /* Getting EIP. */
+ if (!get_old_register_value(memory, cfa, dstate, DWARF_EIP, state, &newstate)) return false;
+ /* Getting EBP. */
+ if (!get_old_register_value(memory, cfa, dstate, DWARF_EBP, state, &newstate)) return false;
+ /* Getting ESP. */
+ if (!get_old_register_value(memory, cfa, dstate, DWARF_ESP, state, &newstate)) return false;
+
+ ALOGV("update_state: IP: 0x%x; restore IP: 0x%x", state->reg[DWARF_EIP], newstate.reg[DWARF_EIP]);
+ ALOGV("update_state: EBP: 0x%x; restore EBP: 0x%x", state->reg[DWARF_EBP], newstate.reg[DWARF_EBP]);
+ ALOGV("update_state: ESP: 0x%x; restore ESP: 0x%x", state->reg[DWARF_ESP], newstate.reg[DWARF_ESP]);
+ *state = newstate;
+ return true;
+}
+
+/* Execute CIE and FDE instructions for FDE found with find_fde. */
+static bool execute_fde(const memory_t* memory,
+ const map_info_t* map_info_list,
+ uintptr_t fde,
+ unwind_state_t* state) {
+ uint32_t fde_length = 0;
+ uint32_t cie_length = 0;
+ uintptr_t cie = 0;
+ uintptr_t cie_offset = 0;
+ cie_info_t cie_i;
+ cie_info_t* cie_info = &cie_i;
+ fde_info_t fde_i;
+ fde_info_t* fde_info = &fde_i;
+ dwarf_state_t dwarf_state;
+ dwarf_state_t* dstate = &dwarf_state;
+ dwarf_state_t stack[DWARF_STATES_STACK];
+ uint8_t stack_ptr = 0;
+
+ memset(dstate, 0, sizeof(dwarf_state_t));
+ memset(cie_info, 0, sizeof(cie_info_t));
+ memset(fde_info, 0, sizeof(fde_info_t));
+
+ /* Read common CIE or FDE area:
+ 1st word is length;
+ 2nd word is ID: 0 for CIE, CIE pointer for FDE.
+ */
+ if (!try_get_word(memory, fde, &fde_length)) {
+ return false;
+ }
+ if ((int32_t)fde_length == -1) {
+ ALOGV("execute_fde: 64-bit dwarf detected, not implemented yet");
+ return false;
+ }
+ if (!try_get_word(memory, fde + 4, &cie_offset)) {
+ return false;
+ }
+ if (cie_offset == 0) {
+ /* This is CIE. We shouldn't be here normally. */
+ cie = fde;
+ cie_length = fde_length;
+ } else {
+ /* Find CIE. */
+ /* Positive cie_offset goes backward from current field. */
+ cie = fde + 4 - cie_offset;
+ if (!try_get_word(memory, cie, &cie_length)) {
+ return false;
+ }
+ if ((int32_t)cie_length == -1) {
+ ALOGV("execute_fde: 64-bit dwarf detected, not implemented yet");
+ return false;
+ }
+ if (!try_get_word(memory, cie + 4, &cie_offset)) {
+ return false;
+ }
+ if (cie_offset != 0) {
+ ALOGV("execute_fde: can't find CIE");
+ return false;
+ }
+ }
+ ALOGV("execute_fde: FDE length: %d", fde_length);
+ ALOGV("execute_fde: CIE pointer: %x", cie);
+ ALOGV("execute_fde: CIE length: %d", cie_length);
+
+ /* Read CIE:
+ Augmentation independent:
+ 1st byte is version;
+ next x bytes is /0 terminated augmentation string;
+ next x bytes is unsigned LEB128 encoded code alignment factor;
+ next x bytes is signed LEB128 encoded data alignment factor;
+ next 1 (CIE version 1) or x (CIE version 3 unsigned LEB128) bytes is return register column;
+ Augmentation dependent:
+ if 'z' next x bytes is unsigned LEB128 encoded augmentation data size;
+ if 'L' next 1 byte is LSDA encoding;
+ if 'R' next 1 byte is FDE encoding;
+ if 'S' CIE represents signal handler stack frame;
+ if 'P' next 1 byte is personality encoding folowed by personality function pointer;
+ Next x bytes is CIE program.
+ */
+
+ uint32_t c = 8;
+ if (!try_get_byte(memory, cie, &cie_info->version, &c)) {
+ return false;
+ }
+ ALOGV("execute_fde: CIE version: %d", cie_info->version);
+ uint8_t ch;
+ do {
+ if (!try_get_byte(memory, cie, &ch, &c)) {
+ return false;
+ }
+ switch (ch) {
+ case '\0': break;
+ case 'z': cie_info->aug_z = 1; break;
+ case 'L': cie_info->aug_L = 1; break;
+ case 'R': cie_info->aug_R = 1; break;
+ case 'S': cie_info->aug_S = 1; break;
+ case 'P': cie_info->aug_P = 1; break;
+ default:
+ ALOGV("execute_fde: Unrecognized CIE augmentation char: '%c'", ch);
+ return false;
+ break;
+ }
+ } while (ch);
+ if (!try_get_uleb128(memory, cie, &cie_info->code_align, &c)) {
+ return false;
+ }
+ if (!try_get_sleb128(memory, cie, &cie_info->data_align, &c)) {
+ return false;
+ }
+ if (cie_info->version >= 3) {
+ if (!try_get_uleb128(memory, cie, &cie_info->reg, &c)) {
+ return false;
+ }
+ } else {
+ if (!try_get_byte(memory, cie, (uint8_t*)&cie_info->reg, &c)) {
+ return false;
+ }
+ }
+ ALOGV("execute_fde: CIE code alignment factor: %d", cie_info->code_align);
+ ALOGV("execute_fde: CIE data alignment factor: %d", cie_info->data_align);
+ if (cie_info->aug_z) {
+ if (!try_get_uleb128(memory, cie, &cie_info->aug_z, &c)) {
+ return false;
+ }
+ }
+ if (cie_info->aug_L) {
+ if (!try_get_byte(memory, cie, &cie_info->aug_L, &c)) {
+ return false;
+ }
+ } else {
+ /* Default encoding. */
+ cie_info->aug_L = DW_EH_PE_absptr;
+ }
+ if (cie_info->aug_R) {
+ if (!try_get_byte(memory, cie, &cie_info->aug_R, &c)) {
+ return false;
+ }
+ } else {
+ /* Default encoding. */
+ cie_info->aug_R = DW_EH_PE_absptr;
+ }
+ if (cie_info->aug_P) {
+ /* Get encoding of personality routine pointer. We don't use it now. */
+ if (!try_get_byte(memory, cie, (uint8_t*)&cie_info->aug_P, &c)) {
+ return false;
+ }
+ /* Get routine pointer. */
+ if (!read_dwarf(memory, cie, &cie_info->aug_P, (uint8_t)cie_info->aug_P, &c)) {
+ return false;
+ }
+ }
+ /* CIE program. */
+ /* Length field itself (4 bytes) is not included into length. */
+ stack[0] = *dstate;
+ stack_ptr = 1;
+ while (c < cie_length + 4) {
+ if (!execute_dwarf(memory, cie, cie_info, dstate, &c, stack, &stack_ptr)) {
+ return false;
}
}
+ /* We went directly to CIE. Normally it shouldn't occur. */
+ if (cie == fde) return true;
+
+ /* Go back to FDE. */
+ c = 8;
+ /* Read FDE:
+ Augmentation independent:
+ next x bytes (encoded as specified in CIE) is FDE starting address;
+ next x bytes (encoded as specified in CIE) is FDE number of instructions covered;
+ Augmentation dependent:
+ if 'z' next x bytes is unsigned LEB128 encoded augmentation data size;
+ if 'L' next x bytes is LSDA pointer (encoded as specified in CIE);
+ Next x bytes is FDE program.
+ */
+ if (!read_dwarf(memory, fde, &fde_info->start, (uint8_t)cie_info->aug_R, &c)) {
+ return false;
+ }
+ dstate->loc = fde_info->start;
+ ALOGV("execute_fde: FDE start: %x", dstate->loc);
+ if (!read_dwarf(memory, fde, &fde_info->length, 0, &c)) {
+ return false;
+ }
+ ALOGV("execute_fde: FDE length: %x", fde_info->length);
+ if (cie_info->aug_z) {
+ if (!try_get_uleb128(memory, fde, &fde_info->aug_z, &c)) {
+ return false;
+ }
+ }
+ if (cie_info->aug_L && cie_info->aug_L != DW_EH_PE_omit) {
+ if (!read_dwarf(memory, fde, &fde_info->aug_L, cie_info->aug_L, &c)) {
+ return false;
+ }
+ }
+ /* FDE program. */
+ /* Length field itself (4 bytes) is not included into length. */
+ /* Save CIE state as 0 element of stack. Used by DW_CFA_restore. */
+ stack[0] = *dstate;
+ stack_ptr = 1;
+ while (c < fde_length + 4 && state->reg[DWARF_EIP] >= dstate->loc) {
+ if (!execute_dwarf(memory, fde, cie_info, dstate, &c, stack, &stack_ptr)) {
+ return false;
+ }
+ ALOGV("IP: %x, LOC: %x", state->reg[DWARF_EIP], dstate->loc);
+ }
+
+ return update_state(memory, state, dstate, cie_info);
+}
+
+static ssize_t unwind_backtrace_common(const memory_t* memory,
+ const map_info_t* map_info_list,
+ unwind_state_t* state, backtrace_frame_t* backtrace,
+ size_t ignore_depth, size_t max_depth) {
+
+ size_t ignored_frames = 0;
+ size_t returned_frames = 0;
+
+ ALOGV("Unwinding tid: %d", memory->tid);
+ ALOGV("IP: %x", state->reg[DWARF_EIP]);
+ ALOGV("BP: %x", state->reg[DWARF_EBP]);
+ ALOGV("SP: %x", state->reg[DWARF_ESP]);
+
+ for (size_t index = 0; returned_frames < max_depth; index++) {
+ uintptr_t fde = find_fde(memory, map_info_list, state->reg[DWARF_EIP]);
+ /* FDE is not found, it may happen if stack is corrupted or calling wrong adress.
+ Getting return address from stack.
+ */
+ if (!fde) {
+ uint32_t ip;
+ ALOGV("trying to restore registers from stack");
+ if (!try_get_word(memory, state->reg[DWARF_EBP] + 4, &ip) ||
+ ip == state->reg[DWARF_EIP]) {
+ ALOGV("can't get IP from stack");
+ break;
+ }
+ /* We've been able to get IP from stack so recording the frame before continue. */
+ backtrace_frame_t* frame = add_backtrace_entry(
+ index ? rewind_pc_arch(memory, state->reg[DWARF_EIP]) : state->reg[DWARF_EIP],
+ backtrace, ignore_depth, max_depth,
+ &ignored_frames, &returned_frames);
+ state->reg[DWARF_EIP] = ip;
+ state->reg[DWARF_ESP] = state->reg[DWARF_EBP] + 8;
+ if (!try_get_word(memory, state->reg[DWARF_EBP], &state->reg[DWARF_EBP])) {
+ ALOGV("can't get EBP from stack");
+ break;
+ }
+ ALOGV("restore IP: %x", state->reg[DWARF_EIP]);
+ ALOGV("restore BP: %x", state->reg[DWARF_EBP]);
+ ALOGV("restore SP: %x", state->reg[DWARF_ESP]);
+ continue;
+ }
+ backtrace_frame_t* frame = add_backtrace_entry(
+ index ? rewind_pc_arch(memory, state->reg[DWARF_EIP]) : state->reg[DWARF_EIP],
+ backtrace, ignore_depth, max_depth,
+ &ignored_frames, &returned_frames);
+
+ uint32_t stack_top = state->reg[DWARF_ESP];
+
+ if (!execute_fde(memory, map_info_list, fde, state)) break;
+
+ if (frame) {
+ frame->stack_top = stack_top;
+ if (stack_top < state->reg[DWARF_ESP]) {
+ frame->stack_size = state->reg[DWARF_ESP] - stack_top;
+ }
+ }
+ ALOGV("Stack: 0x%x ... 0x%x - %d bytes", frame->stack_top, state->reg[DWARF_ESP], frame->stack_size);
+ }
return returned_frames;
}
@@ -128,9 +824,15 @@
const ucontext_t* uc = (const ucontext_t*)sigcontext;
unwind_state_t state;
- state.ebp = uc->uc_mcontext.gregs[REG_EBP];
- state.esp = uc->uc_mcontext.gregs[REG_ESP];
- state.eip = uc->uc_mcontext.gregs[REG_EIP];
+#if defined(__APPLE__)
+ state.reg[DWARF_EBP] = uc->uc_mcontext->__ss.__ebp;
+ state.reg[DWARF_ESP] = uc->uc_mcontext->__ss.__esp;
+ state.reg[DWARF_EIP] = uc->uc_mcontext->__ss.__eip;
+#else
+ state.reg[DWARF_EBP] = uc->uc_mcontext.gregs[REG_EBP];
+ state.reg[DWARF_ESP] = uc->uc_mcontext.gregs[REG_ESP];
+ state.reg[DWARF_EIP] = uc->uc_mcontext.gregs[REG_EIP];
+#endif
memory_t memory;
init_memory(&memory, map_info_list);
@@ -140,18 +842,22 @@
ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context,
backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) {
+#if defined(__APPLE__)
+ return -1;
+#else
pt_regs_x86_t regs;
if (ptrace(PTRACE_GETREGS, tid, 0, ®s)) {
return -1;
}
unwind_state_t state;
- state.ebp = regs.ebp;
- state.eip = regs.eip;
- state.esp = regs.esp;
+ state.reg[DWARF_EBP] = regs.ebp;
+ state.reg[DWARF_EIP] = regs.eip;
+ state.reg[DWARF_ESP] = regs.esp;
memory_t memory;
init_memory_ptrace(&memory, tid);
return unwind_backtrace_common(&memory, context->map_info_list,
&state, backtrace, ignore_depth, max_depth);
+#endif
}
diff --git a/libcorkscrew/arch-x86/dwarf.h b/libcorkscrew/arch-x86/dwarf.h
new file mode 100755
index 0000000..962fc55
--- /dev/null
+++ b/libcorkscrew/arch-x86/dwarf.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dwarf2 data encoding flags.
+ */
+
+#define DW_EH_PE_absptr 0x00
+#define DW_EH_PE_omit 0xff
+#define DW_EH_PE_uleb128 0x01
+#define DW_EH_PE_udata2 0x02
+#define DW_EH_PE_udata4 0x03
+#define DW_EH_PE_udata8 0x04
+#define DW_EH_PE_sleb128 0x09
+#define DW_EH_PE_sdata2 0x0A
+#define DW_EH_PE_sdata4 0x0B
+#define DW_EH_PE_sdata8 0x0C
+#define DW_EH_PE_signed 0x08
+#define DW_EH_PE_pcrel 0x10
+#define DW_EH_PE_textrel 0x20
+#define DW_EH_PE_datarel 0x30
+#define DW_EH_PE_funcrel 0x40
+#define DW_EH_PE_aligned 0x50
+#define DW_EH_PE_indirect 0x80
+
+/*
+ * Dwarf2 call frame instructions.
+ */
+
+typedef enum {
+ 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
+} dwarf_CFA;
+
+/*
+ * eh_frame_hdr information.
+*/
+
+typedef struct {
+ uint8_t version;
+ uint8_t eh_frame_ptr_enc;
+ uint8_t fde_count_enc;
+ uint8_t fde_table_enc;
+ uintptr_t eh_frame_ptr;
+ uint32_t fde_count;
+} eh_frame_hdr_info_t;
+
+/*
+ * CIE information.
+*/
+
+typedef struct {
+ uint8_t version;
+ uint32_t code_align;
+ uint32_t data_align;
+ uint32_t reg;
+ uint32_t aug_z;
+ uint8_t aug_L;
+ uint8_t aug_R;
+ uint8_t aug_S;
+ uint32_t aug_P;
+} cie_info_t;
+
+/*
+ * FDE information.
+*/
+
+typedef struct {
+ uint32_t start;
+ uint32_t length; // number of instructions covered by FDE
+ uint32_t aug_z;
+ uint32_t aug_L;
+} fde_info_t;
+
+/*
+ * Dwarf state.
+*/
+
+/* Stack of states: required for DW_CFA_remember_state/DW_CFA_restore_state
+ 30 should be enough */
+#define DWARF_STATES_STACK 30
+
+typedef struct {
+ char rule; // rule: o - offset(value); r - register(value)
+ uint32_t value; // value
+} reg_rule_t;
+
+/* Dwarf preserved number of registers for x86. */
+
+#define DWARF_REGISTERS 17
+
+typedef struct {
+ uintptr_t loc; // location (ip)
+ uint8_t cfa_reg; // index of register where CFA location stored
+ intptr_t cfa_off; // offset
+ reg_rule_t regs[DWARF_REGISTERS]; // dwarf preserved registers for x86
+} dwarf_state_t;
+
+/* DWARF registers we are caring about. */
+
+#define DWARF_EAX 0
+#define DWARF_ECX 1
+#define DWARF_EDX 2
+#define DWARF_EBX 3
+#define DWARF_ESP 4
+#define DWARF_EBP 5
+#define DWARF_ESI 6
+#define DWARF_EDI 7
+#define DWARF_EIP 8
+
+
diff --git a/libcorkscrew/arch-x86/ptrace-x86.c b/libcorkscrew/arch-x86/ptrace-x86.c
old mode 100644
new mode 100755
index 07cfd3a..9c49b93
--- a/libcorkscrew/arch-x86/ptrace-x86.c
+++ b/libcorkscrew/arch-x86/ptrace-x86.c
@@ -19,11 +19,44 @@
#include "../ptrace-arch.h"
+#include <stddef.h>
+#include <elf.h>
#include <cutils/log.h>
-void load_ptrace_map_info_data_arch(pid_t pid __attribute__((unused)),
- map_info_t* mi __attribute__((unused)),
- map_info_data_t* data __attribute__((unused))) {
+static void load_eh_frame_hdr(pid_t pid, map_info_t* mi, uintptr_t *eh_frame_hdr) {
+ uint32_t elf_phoff;
+ uint32_t elf_phentsize_ehsize;
+ uint32_t elf_shentsize_phnum;
+ if (try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phoff), &elf_phoff)
+ && try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_ehsize),
+ &elf_phentsize_ehsize)
+ && try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phnum),
+ &elf_shentsize_phnum)) {
+ uint32_t elf_phentsize = elf_phentsize_ehsize >> 16;
+ uint32_t elf_phnum = elf_shentsize_phnum & 0xffff;
+ for (uint32_t i = 0; i < elf_phnum; i++) {
+ uintptr_t elf_phdr = mi->start + elf_phoff + i * elf_phentsize;
+ uint32_t elf_phdr_type;
+ if (!try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_type), &elf_phdr_type)) {
+ break;
+ }
+ if (elf_phdr_type == PT_GNU_EH_FRAME) {
+ uint32_t elf_phdr_offset;
+ if (!try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_offset),
+ &elf_phdr_offset)) {
+ break;
+ }
+ *eh_frame_hdr = mi->start + elf_phdr_offset;
+ ALOGV("Parsed .eh_frame_hdr info for %s: start=0x%08x", mi->name, *eh_frame_hdr);
+ return;
+ }
+ }
+ }
+ *eh_frame_hdr = 0;
+}
+
+void load_ptrace_map_info_data_arch(pid_t pid, map_info_t* mi, map_info_data_t* data) {
+ load_eh_frame_hdr(pid, mi, &data->eh_frame_hdr);
}
void free_ptrace_map_info_data_arch(map_info_t* mi __attribute__((unused)),
diff --git a/libcorkscrew/backtrace.c b/libcorkscrew/backtrace.c
index 3697d18..b365e5b 100644
--- a/libcorkscrew/backtrace.c
+++ b/libcorkscrew/backtrace.c
@@ -33,18 +33,24 @@
#include <unwind.h>
#include <cutils/log.h>
#include <cutils/atomic.h>
-#include <elf.h>
-#if HAVE_DLADDR
#define __USE_GNU // For dladdr(3) in glibc.
#include <dlfcn.h>
-#endif
#if defined(__BIONIC__)
// Bionic implements and exports gettid but only implements tgkill.
extern int tgkill(int tgid, int tid, int sig);
+#elif defined(__APPLE__)
+
+#include <sys/syscall.h>
+
+// Mac OS >= 10.6 has a system call equivalent to Linux's gettid().
+static pid_t gettid() {
+ return syscall(SYS_thread_selfid);
+}
+
#else
// glibc doesn't implement or export either gettid or tgkill.
@@ -99,7 +105,7 @@
state.returned_frames = 0;
init_memory(&state.memory, milist);
- _Unwind_Reason_Code rc =_Unwind_Backtrace(unwind_backtrace_callback, &state);
+ _Unwind_Reason_Code rc = _Unwind_Backtrace(unwind_backtrace_callback, &state);
release_my_map_info_list(milist);
@@ -148,7 +154,9 @@
ALOGV("Unwinding thread %d from thread %d.", tid, gettid());
-#ifdef CORKSCREW_HAVE_ARCH
+ // TODO: there's no tgkill(2) on Mac OS, so we'd either need the
+ // mach_port_t or the pthread_t rather than the tid.
+#if defined(CORKSCREW_HAVE_ARCH) && !defined(__APPLE__)
struct sigaction act;
struct sigaction oact;
memset(&act, 0, sizeof(act));
@@ -256,7 +264,6 @@
if (mi->name[0]) {
symbol->map_name = strdup(mi->name);
}
-#if HAVE_DLADDR
Dl_info info;
if (dladdr((const void*)frame->absolute_pc, &info) && info.dli_sname) {
symbol->relative_symbol_addr = (uintptr_t)info.dli_saddr
@@ -264,7 +271,6 @@
symbol->symbol_name = strdup(info.dli_sname);
symbol->demangled_name = demangle_symbol_name(symbol->symbol_name);
}
-#endif
}
}
release_my_map_info_list(milist);
@@ -309,20 +315,20 @@
const backtrace_symbol_t* symbol, char* buffer, size_t bufferSize) {
const char* mapName = symbol->map_name ? symbol->map_name : "<unknown>";
const char* symbolName = symbol->demangled_name ? symbol->demangled_name : symbol->symbol_name;
- size_t fieldWidth = (bufferSize - 80) / 2;
+ int fieldWidth = (bufferSize - 80) / 2;
if (symbolName) {
uint32_t pc_offset = symbol->relative_pc - symbol->relative_symbol_addr;
if (pc_offset) {
- snprintf(buffer, bufferSize, "#%02d pc %08x %.*s (%.*s+%u)",
- frameNumber, symbol->relative_pc, fieldWidth, mapName,
+ snprintf(buffer, bufferSize, "#%02u pc %p %.*s (%.*s+%u)",
+ frameNumber, (void*) symbol->relative_pc, fieldWidth, mapName,
fieldWidth, symbolName, pc_offset);
} else {
- snprintf(buffer, bufferSize, "#%02d pc %08x %.*s (%.*s)",
- frameNumber, symbol->relative_pc, fieldWidth, mapName,
+ snprintf(buffer, bufferSize, "#%02u pc %p %.*s (%.*s)",
+ frameNumber, (void*) symbol->relative_pc, fieldWidth, mapName,
fieldWidth, symbolName);
}
} else {
- snprintf(buffer, bufferSize, "#%02d pc %08x %.*s",
- frameNumber, symbol->relative_pc, fieldWidth, mapName);
+ snprintf(buffer, bufferSize, "#%02u pc %p %.*s",
+ frameNumber, (void*) symbol->relative_pc, fieldWidth, mapName);
}
}
diff --git a/libcorkscrew/demangle.c b/libcorkscrew/demangle.c
index 54247cb..30ab1b0 100644
--- a/libcorkscrew/demangle.c
+++ b/libcorkscrew/demangle.c
@@ -25,6 +25,12 @@
int *status);
char* demangle_symbol_name(const char* name) {
+#if defined(__APPLE__)
+ // Mac OS' __cxa_demangle demangles "f" as "float"; last tested on 10.7.
+ if (name != NULL && name[0] != '_') {
+ return NULL;
+ }
+#endif
// __cxa_demangle handles NULL by returning NULL
return __cxa_demangle(name, 0, 0, 0);
}
diff --git a/libcorkscrew/map_info.c b/libcorkscrew/map_info.c
index 6a27664..93dffbf 100644
--- a/libcorkscrew/map_info.c
+++ b/libcorkscrew/map_info.c
@@ -29,6 +29,67 @@
#include <cutils/log.h>
#include <sys/time.h>
+#if defined(__APPLE__)
+
+// Mac OS vmmap(1) output:
+// __TEXT 0009f000-000a1000 [ 8K 8K] r-x/rwx SM=COW /Volumes/android/dalvik-dev/out/host/darwin-x86/bin/libcorkscrew_test\n
+// 012345678901234567890123456789012345678901234567890123456789
+// 0 1 2 3 4 5
+static map_info_t* parse_vmmap_line(const char* line) {
+ unsigned long int start;
+ unsigned long int end;
+ char permissions[4];
+ int name_pos;
+ if (sscanf(line, "%*21c %lx-%lx [%*13c] %3c/%*3c SM=%*3c %n",
+ &start, &end, permissions, &name_pos) != 3) {
+ return NULL;
+ }
+
+ const char* name = line + name_pos;
+ size_t name_len = strlen(name);
+
+ map_info_t* mi = calloc(1, sizeof(map_info_t) + name_len);
+ if (mi != NULL) {
+ mi->start = start;
+ mi->end = end;
+ mi->is_readable = permissions[0] == 'r';
+ mi->is_writable = permissions[1] == 'w';
+ mi->is_executable = permissions[2] == 'x';
+ mi->data = NULL;
+ memcpy(mi->name, name, name_len);
+ mi->name[name_len - 1] = '\0';
+ ALOGV("Parsed map: start=0x%08x, end=0x%08x, "
+ "is_readable=%d, is_writable=%d is_executable=%d, name=%s",
+ mi->start, mi->end,
+ mi->is_readable, mi->is_writable, mi->is_executable, mi->name);
+ }
+ return mi;
+}
+
+map_info_t* load_map_info_list(pid_t pid) {
+ char cmd[1024];
+ snprintf(cmd, sizeof(cmd), "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid);
+ FILE* fp = popen(cmd, "r");
+ if (fp == NULL) {
+ return NULL;
+ }
+
+ char line[1024];
+ map_info_t* milist = NULL;
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ map_info_t* mi = parse_vmmap_line(line);
+ if (mi != NULL) {
+ mi->next = milist;
+ milist = mi;
+ }
+ }
+ pclose(fp);
+ return milist;
+}
+
+#else
+
+// Linux /proc/<pid>/maps lines:
// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so\n
// 012345678901234567890123456789012345678901234567890123456789
// 0 1 2 3 4 5
@@ -63,9 +124,9 @@
memcpy(mi->name, name, name_len);
mi->name[name_len] = '\0';
ALOGV("Parsed map: start=0x%08x, end=0x%08x, "
- "is_readable=%d, is_writable=%d, is_executable=%d, name=%s",
- mi->start, mi->end,
- mi->is_readable, mi->is_writable, mi->is_executable, mi->name);
+ "is_readable=%d, is_writable=%d, is_executable=%d, name=%s",
+ mi->start, mi->end,
+ mi->is_readable, mi->is_writable, mi->is_executable, mi->name);
}
return mi;
}
@@ -91,6 +152,8 @@
return milist;
}
+#endif
+
void free_map_info_list(map_info_t* milist) {
while (milist) {
map_info_t* next = milist->next;
@@ -132,11 +195,17 @@
int64_t timestamp;
} my_map_info_data_t;
-static int64_t now() {
+static int64_t now_ns() {
+#if defined(HAVE_POSIX_CLOCKS)
struct timespec t;
t.tv_sec = t.tv_nsec = 0;
clock_gettime(CLOCK_MONOTONIC, &t);
return t.tv_sec * 1000000000LL + t.tv_nsec;
+#else
+ struct timeval t;
+ gettimeofday(&t, NULL);
+ return t.tv_sec * 1000000000LL + t.tv_usec * 1000LL;
+#endif
}
static void dec_ref(map_info_t* milist, my_map_info_data_t* data) {
@@ -150,8 +219,8 @@
map_info_t* acquire_my_map_info_list() {
pthread_mutex_lock(&g_my_map_info_list_mutex);
- int64_t time = now();
- if (g_my_map_info_list) {
+ int64_t time = now_ns();
+ if (g_my_map_info_list != NULL) {
my_map_info_data_t* data = (my_map_info_data_t*)g_my_map_info_list->data;
int64_t age = time - data->timestamp;
if (age >= MAX_CACHE_AGE) {
@@ -163,10 +232,10 @@
}
}
- if (!g_my_map_info_list) {
+ if (g_my_map_info_list == NULL) {
my_map_info_data_t* data = (my_map_info_data_t*)malloc(sizeof(my_map_info_data_t));
g_my_map_info_list = load_map_info_list(getpid());
- if (g_my_map_info_list) {
+ if (g_my_map_info_list != NULL) {
ALOGV("Loaded my_map_info_list %p.", g_my_map_info_list);
g_my_map_info_list->data = data;
data->refs = 1;
@@ -196,3 +265,15 @@
pthread_mutex_unlock(&g_my_map_info_list_mutex);
}
}
+
+void flush_my_map_info_list() {
+ pthread_mutex_lock(&g_my_map_info_list_mutex);
+
+ if (g_my_map_info_list != NULL) {
+ my_map_info_data_t* data = (my_map_info_data_t*) g_my_map_info_list->data;
+ dec_ref(g_my_map_info_list, data);
+ g_my_map_info_list = NULL;
+ }
+
+ pthread_mutex_unlock(&g_my_map_info_list_mutex);
+}
diff --git a/libcorkscrew/ptrace-arch.h b/libcorkscrew/ptrace-arch.h
old mode 100644
new mode 100755
index c02df52..4451c29
--- a/libcorkscrew/ptrace-arch.h
+++ b/libcorkscrew/ptrace-arch.h
@@ -33,6 +33,8 @@
#ifdef __arm__
uintptr_t exidx_start;
size_t exidx_size;
+#elif __i386__
+ uintptr_t eh_frame_hdr;
#endif
symbol_table_t* symbol_table;
} map_info_data_t;
diff --git a/libcorkscrew/ptrace.c b/libcorkscrew/ptrace.c
index 6496d5e..be58f7f 100644
--- a/libcorkscrew/ptrace.c
+++ b/libcorkscrew/ptrace.c
@@ -46,21 +46,25 @@
}
bool try_get_word(const memory_t* memory, uintptr_t ptr, uint32_t* out_value) {
- ALOGV("try_get_word: reading word at 0x%08x", ptr);
+ ALOGV("try_get_word: reading word at %p", (void*) ptr);
if (ptr & 3) {
- ALOGV("try_get_word: invalid pointer 0x%08x", ptr);
+ ALOGV("try_get_word: invalid pointer %p", (void*) ptr);
*out_value = 0xffffffffL;
return false;
}
if (memory->tid < 0) {
if (!is_readable_map(memory->map_info_list, ptr)) {
- ALOGV("try_get_word: pointer 0x%08x not in a readable map", ptr);
+ ALOGV("try_get_word: pointer %p not in a readable map", (void*) ptr);
*out_value = 0xffffffffL;
return false;
}
*out_value = *(uint32_t*)ptr;
return true;
} else {
+#if defined(__APPLE__)
+ ALOGV("no ptrace on Mac OS");
+ return false;
+#else
// ptrace() returns -1 and sets errno when the operation fails.
// To disambiguate -1 from a valid result, we clear errno beforehand.
errno = 0;
@@ -71,6 +75,7 @@
return false;
}
return true;
+#endif
}
}
@@ -129,6 +134,7 @@
free_ptrace_map_info_data(mi);
}
free_map_info_list(context->map_info_list);
+ free(context);
}
void find_symbol_ptrace(const ptrace_context_t* context,
diff --git a/libcorkscrew/symbol_table.c b/libcorkscrew/symbol_table.c
index 29e4a79..982ccc8 100644
--- a/libcorkscrew/symbol_table.c
+++ b/libcorkscrew/symbol_table.c
@@ -21,13 +21,17 @@
#include <stdbool.h>
#include <stdlib.h>
-#include <elf.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <cutils/log.h>
+#if defined(__APPLE__)
+#else
+
+#include <elf.h>
+
static bool is_elf(Elf32_Ehdr* e) {
return (e->e_ident[EI_MAG0] == ELFMAG0 &&
e->e_ident[EI_MAG1] == ELFMAG1 &&
@@ -35,6 +39,8 @@
e->e_ident[EI_MAG3] == ELFMAG3);
}
+#endif
+
// Compare function for qsort
static int qcompar(const void *a, const void *b) {
const symbol_t* asym = (const symbol_t*)a;
@@ -55,6 +61,7 @@
symbol_table_t* load_symbol_table(const char *filename) {
symbol_table_t* table = NULL;
+#if !defined(__APPLE__)
ALOGV("Loading symbol table from '%s'.", filename);
int fd = open(filename, O_RDONLY);
@@ -197,6 +204,7 @@
out_close:
close(fd);
+#endif
out:
return table;
diff --git a/libcorkscrew/test.c b/libcorkscrew/test.cpp
similarity index 77%
rename from libcorkscrew/test.c
rename to libcorkscrew/test.cpp
index af34c03..22dfa7d 100644
--- a/libcorkscrew/test.c
+++ b/libcorkscrew/test.cpp
@@ -3,11 +3,14 @@
#include <stdio.h>
#include <stdlib.h>
-void do_backtrace() {
+int do_backtrace(float /* just to test demangling */) {
const size_t MAX_DEPTH = 32;
backtrace_frame_t* frames = (backtrace_frame_t*) malloc(sizeof(backtrace_frame_t) * MAX_DEPTH);
ssize_t frame_count = unwind_backtrace(frames, 0, MAX_DEPTH);
fprintf(stderr, "frame_count=%d\n", (int) frame_count);
+ if (frame_count <= 0) {
+ return frame_count;
+ }
backtrace_symbol_t* backtrace_symbols = (backtrace_symbol_t*) malloc(sizeof(backtrace_symbol_t) * frame_count);
get_backtrace_symbols(frames, frame_count, backtrace_symbols);
@@ -31,7 +34,7 @@
symbol = find_symbol(symbols, frames[i].absolute_pc);
}
if (symbol != NULL) {
- uintptr_t offset = frames[i].absolute_pc - symbol->start;
+ int offset = frames[i].absolute_pc - symbol->start;
fprintf(stderr, " %s (%s%+d)\n", line, symbol->name, offset);
} else {
fprintf(stderr, " %s (\?\?\?)\n", line);
@@ -43,22 +46,31 @@
free_backtrace_symbols(backtrace_symbols, frame_count);
free(backtrace_symbols);
free(frames);
+ return frame_count;
}
-__attribute__ ((noinline)) void g() {
- fprintf(stderr, "g()\n");
- do_backtrace();
-}
+struct C {
+ int g(int i);
+};
-__attribute__ ((noinline)) int f(int i) {
- fprintf(stderr, "f(%i)\n", i);
+__attribute__ ((noinline)) int C::g(int i) {
if (i == 0) {
- g();
- return 0;
+ return do_backtrace(0.1);
}
- return f(i - 1);
+ return g(i - 1);
+}
+
+extern "C" __attribute__ ((noinline)) int f() {
+ C c;
+ return c.g(5);
}
int main() {
- return f(5);
+ flush_my_map_info_list();
+ f();
+
+ flush_my_map_info_list();
+ f();
+
+ return 0;
}
diff --git a/libcutils/Android.mk b/libcutils/Android.mk
index d9bd8d8..17b320f 100644
--- a/libcutils/Android.mk
+++ b/libcutils/Android.mk
@@ -77,12 +77,8 @@
abort_socket.c \
fs.c \
selector.c \
- tztime.c \
multiuser.c \
zygote.c
-
- commonHostSources += \
- tzstrftime.c
endif
@@ -134,7 +130,11 @@
LOCAL_CFLAGS += -DHAVE_MEMSET16 -DHAVE_MEMSET32
LOCAL_SRC_FILES += arch-x86/android_memset16.S arch-x86/android_memset32.S memory.c
else # !x86-atom
+ifeq ($(TARGET_ARCH),mips)
+LOCAL_SRC_FILES += arch-mips/android_memset.c
+else # !mips
LOCAL_SRC_FILES += memory.c
+endif # !mips
endif # !x86-atom
endif # !arm
@@ -158,3 +158,5 @@
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_MODULE_TAGS := optional
include $(BUILD_EXECUTABLE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/libcutils/arch-mips/android_memset.c b/libcutils/arch-mips/android_memset.c
new file mode 100644
index 0000000..bbc99fe
--- /dev/null
+++ b/libcutils/arch-mips/android_memset.c
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cutils/memory.h>
+
+/* Use mips-assembler versions supplied by bionic/libc/arch-mips/string/memset.S: */
+void _memset16(uint16_t* dst, uint16_t value, size_t size);
+void _memset32(uint32_t* dst, uint32_t value, size_t size);
+
+void android_memset16(uint16_t* dst, uint16_t value, size_t size)
+{
+ _memset16(dst, value, size);
+}
+
+void android_memset32(uint32_t* dst, uint32_t value, size_t size)
+{
+ _memset32(dst, value, size);
+}
diff --git a/libcutils/private.h b/libcutils/private.h
deleted file mode 100644
index 2837b70..0000000
--- a/libcutils/private.h
+++ /dev/null
@@ -1,368 +0,0 @@
-#ifndef PRIVATE_H
-
-#define PRIVATE_H
-
-/*
-** This file is in the public domain, so clarified as of
-** 1996-06-05 by Arthur David Olson.
-*/
-
-/*
-** This header is for use ONLY with the time conversion code.
-** There is no guarantee that it will remain unchanged,
-** or that it will remain at all.
-** Do NOT copy it to any system include directory.
-** Thank you!
-*/
-
-/*
-** ID
-*/
-
-#ifndef lint
-#ifndef NOID
-static char privatehid[] = "@(#)private.h 8.2";
-#endif /* !defined NOID */
-#endif /* !defined lint */
-
-#define GRANDPARENTED "Local time zone must be set--see zic manual page"
-
-/*
-** Defaults for preprocessor symbols.
-** You can override these in your C compiler options, e.g. `-DHAVE_ADJTIME=0'.
-*/
-
-#ifndef HAVE_ADJTIME
-#define HAVE_ADJTIME 1
-#endif /* !defined HAVE_ADJTIME */
-
-#ifndef HAVE_GETTEXT
-#define HAVE_GETTEXT 0
-#endif /* !defined HAVE_GETTEXT */
-
-#ifndef HAVE_INCOMPATIBLE_CTIME_R
-#define HAVE_INCOMPATIBLE_CTIME_R 0
-#endif /* !defined INCOMPATIBLE_CTIME_R */
-
-#ifndef HAVE_SETTIMEOFDAY
-#define HAVE_SETTIMEOFDAY 3
-#endif /* !defined HAVE_SETTIMEOFDAY */
-
-#ifndef HAVE_STRERROR
-#define HAVE_STRERROR 1
-#endif /* !defined HAVE_STRERROR */
-
-#ifndef HAVE_SYMLINK
-#define HAVE_SYMLINK 1
-#endif /* !defined HAVE_SYMLINK */
-
-#ifndef HAVE_SYS_STAT_H
-#define HAVE_SYS_STAT_H 1
-#endif /* !defined HAVE_SYS_STAT_H */
-
-#ifndef HAVE_SYS_WAIT_H
-#define HAVE_SYS_WAIT_H 1
-#endif /* !defined HAVE_SYS_WAIT_H */
-
-#ifndef HAVE_UNISTD_H
-#define HAVE_UNISTD_H 1
-#endif /* !defined HAVE_UNISTD_H */
-
-#ifndef HAVE_UTMPX_H
-#define HAVE_UTMPX_H 0
-#endif /* !defined HAVE_UTMPX_H */
-
-#ifndef LOCALE_HOME
-#define LOCALE_HOME "/usr/lib/locale"
-#endif /* !defined LOCALE_HOME */
-
-#if HAVE_INCOMPATIBLE_CTIME_R
-#define asctime_r _incompatible_asctime_r
-#define ctime_r _incompatible_ctime_r
-#endif /* HAVE_INCOMPATIBLE_CTIME_R */
-
-/*
-** Nested includes
-*/
-
-#include "sys/types.h" /* for time_t */
-#include "stdio.h"
-#include "errno.h"
-#include "string.h"
-#include "limits.h" /* for CHAR_BIT et al. */
-#include "time.h"
-#include "stdlib.h"
-
-#if HAVE_GETTEXT
-#include "libintl.h"
-#endif /* HAVE_GETTEXT */
-
-#if HAVE_SYS_WAIT_H
-#include <sys/wait.h> /* for WIFEXITED and WEXITSTATUS */
-#endif /* HAVE_SYS_WAIT_H */
-
-#ifndef WIFEXITED
-#define WIFEXITED(status) (((status) & 0xff) == 0)
-#endif /* !defined WIFEXITED */
-#ifndef WEXITSTATUS
-#define WEXITSTATUS(status) (((status) >> 8) & 0xff)
-#endif /* !defined WEXITSTATUS */
-
-#if HAVE_UNISTD_H
-#include "unistd.h" /* for F_OK and R_OK */
-#endif /* HAVE_UNISTD_H */
-
-#if !HAVE_UNISTD_H
-#ifndef F_OK
-#define F_OK 0
-#endif /* !defined F_OK */
-#ifndef R_OK
-#define R_OK 4
-#endif /* !defined R_OK */
-#endif /* !HAVE_UNISTD_H */
-
-/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
-#define is_digit(c) ((unsigned)(c) - '0' <= 9)
-
-/*
-** Define HAVE_STDINT_H's default value here, rather than at the
-** start, since __GLIBC__'s value depends on previously-included
-** files.
-** (glibc 2.1 and later have stdint.h, even with pre-C99 compilers.)
-*/
-#ifndef HAVE_STDINT_H
-#define HAVE_STDINT_H \
- (199901 <= __STDC_VERSION__ || \
- 2 < (__GLIBC__ + (0 < __GLIBC_MINOR__)))
-#endif /* !defined HAVE_STDINT_H */
-
-#if HAVE_STDINT_H
-#include "stdint.h"
-#endif /* !HAVE_STDINT_H */
-
-#ifndef INT_FAST64_MAX
-/* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_MAX. */
-#if defined LLONG_MAX || defined __LONG_LONG_MAX__
-typedef long long int_fast64_t;
-#else /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */
-#if (LONG_MAX >> 31) < 0xffffffff
-Please use a compiler that supports a 64-bit integer type (or wider);
-you may need to compile with "-DHAVE_STDINT_H".
-#endif /* (LONG_MAX >> 31) < 0xffffffff */
-typedef long int_fast64_t;
-#endif /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */
-#endif /* !defined INT_FAST64_MAX */
-
-#ifndef INT32_MAX
-#define INT32_MAX 0x7fffffff
-#endif /* !defined INT32_MAX */
-#ifndef INT32_MIN
-#define INT32_MIN (-1 - INT32_MAX)
-#endif /* !defined INT32_MIN */
-
-/*
-** Workarounds for compilers/systems.
-*/
-
-/*
-** If your compiler lacks prototypes, "#define P(x) ()".
-*/
-
-#ifndef P
-#define P(x) x
-#endif /* !defined P */
-
-/*
-** SunOS 4.1.1 headers lack EXIT_SUCCESS.
-*/
-
-#ifndef EXIT_SUCCESS
-#define EXIT_SUCCESS 0
-#endif /* !defined EXIT_SUCCESS */
-
-/*
-** SunOS 4.1.1 headers lack EXIT_FAILURE.
-*/
-
-#ifndef EXIT_FAILURE
-#define EXIT_FAILURE 1
-#endif /* !defined EXIT_FAILURE */
-
-/*
-** SunOS 4.1.1 headers lack FILENAME_MAX.
-*/
-
-#ifndef FILENAME_MAX
-
-#ifndef MAXPATHLEN
-#ifdef unix
-#include "sys/param.h"
-#endif /* defined unix */
-#endif /* !defined MAXPATHLEN */
-
-#ifdef MAXPATHLEN
-#define FILENAME_MAX MAXPATHLEN
-#endif /* defined MAXPATHLEN */
-#ifndef MAXPATHLEN
-#define FILENAME_MAX 1024 /* Pure guesswork */
-#endif /* !defined MAXPATHLEN */
-
-#endif /* !defined FILENAME_MAX */
-
-/*
-** SunOS 4.1.1 libraries lack remove.
-*/
-
-#ifndef remove
-extern int unlink P((const char * filename));
-#define remove unlink
-#endif /* !defined remove */
-
-/*
-** Some ancient errno.h implementations don't declare errno.
-** But some newer errno.h implementations define it as a macro.
-** Fix the former without affecting the latter.
-*/
-
-#ifndef errno
-extern int errno;
-#endif /* !defined errno */
-
-/*
-** Some time.h implementations don't declare asctime_r.
-** Others might define it as a macro.
-** Fix the former without affecting the latter.
-*/
-
-#ifndef asctime_r
-extern char * asctime_r();
-#endif
-
-/*
-** Private function declarations.
-*/
-
-char * icalloc P((int nelem, int elsize));
-char * icatalloc P((char * old, const char * new));
-char * icpyalloc P((const char * string));
-char * imalloc P((int n));
-void * irealloc P((void * pointer, int size));
-void icfree P((char * pointer));
-void ifree P((char * pointer));
-const char * scheck P((const char * string, const char * format));
-
-/*
-** Finally, some convenience items.
-*/
-
-#ifndef TRUE
-#define TRUE 1
-#endif /* !defined TRUE */
-
-#ifndef FALSE
-#define FALSE 0
-#endif /* !defined FALSE */
-
-#ifndef TYPE_BIT
-#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT)
-#endif /* !defined TYPE_BIT */
-
-#ifndef TYPE_SIGNED
-#define TYPE_SIGNED(type) (((type) -1) < 0)
-#endif /* !defined TYPE_SIGNED */
-
-/*
-** Since the definition of TYPE_INTEGRAL contains floating point numbers,
-** it cannot be used in preprocessor directives.
-*/
-
-#ifndef TYPE_INTEGRAL
-#define TYPE_INTEGRAL(type) (((type) 0.5) != 0.5)
-#endif /* !defined TYPE_INTEGRAL */
-
-#ifndef INT_STRLEN_MAXIMUM
-/*
-** 302 / 1000 is log10(2.0) rounded up.
-** Subtract one for the sign bit if the type is signed;
-** add one for integer division truncation;
-** add one more for a minus sign if the type is signed.
-*/
-#define INT_STRLEN_MAXIMUM(type) \
- ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + \
- 1 + TYPE_SIGNED(type))
-#endif /* !defined INT_STRLEN_MAXIMUM */
-
-/*
-** INITIALIZE(x)
-*/
-
-#ifndef GNUC_or_lint
-#ifdef lint
-#define GNUC_or_lint
-#endif /* defined lint */
-#ifndef lint
-#ifdef __GNUC__
-#define GNUC_or_lint
-#endif /* defined __GNUC__ */
-#endif /* !defined lint */
-#endif /* !defined GNUC_or_lint */
-
-#ifndef INITIALIZE
-#ifdef GNUC_or_lint
-#define INITIALIZE(x) ((x) = 0)
-#endif /* defined GNUC_or_lint */
-#ifndef GNUC_or_lint
-#define INITIALIZE(x)
-#endif /* !defined GNUC_or_lint */
-#endif /* !defined INITIALIZE */
-
-/*
-** For the benefit of GNU folk...
-** `_(MSGID)' uses the current locale's message library string for MSGID.
-** The default is to use gettext if available, and use MSGID otherwise.
-*/
-
-#ifndef _
-#if HAVE_GETTEXT
-#define _(msgid) gettext(msgid)
-#else /* !HAVE_GETTEXT */
-#define _(msgid) msgid
-#endif /* !HAVE_GETTEXT */
-#endif /* !defined _ */
-
-#ifndef TZ_DOMAIN
-#define TZ_DOMAIN "tz"
-#endif /* !defined TZ_DOMAIN */
-
-#if HAVE_INCOMPATIBLE_CTIME_R
-#undef asctime_r
-#undef ctime_r
-char *asctime_r P((struct tm const *, char *));
-char *ctime_r P((time_t const *, char *));
-#endif /* HAVE_INCOMPATIBLE_CTIME_R */
-
-#ifndef YEARSPERREPEAT
-#define YEARSPERREPEAT 400 /* years before a Gregorian repeat */
-#endif /* !defined YEARSPERREPEAT */
-
-/*
-** The Gregorian year averages 365.2425 days, which is 31556952 seconds.
-*/
-
-#ifndef AVGSECSPERYEAR
-#define AVGSECSPERYEAR 31556952L
-#endif /* !defined AVGSECSPERYEAR */
-
-#ifndef SECSPERREPEAT
-#define SECSPERREPEAT ((int_fast64_t) YEARSPERREPEAT * (int_fast64_t) AVGSECSPERYEAR)
-#endif /* !defined SECSPERREPEAT */
-
-#ifndef SECSPERREPEAT_BITS
-#define SECSPERREPEAT_BITS 34 /* ceil(log2(SECSPERREPEAT)) */
-#endif /* !defined SECSPERREPEAT_BITS */
-
-/*
-** UNIX was a registered trademark of The Open Group in 2003.
-*/
-
-#endif /* !defined PRIVATE_H */
diff --git a/libcutils/properties.c b/libcutils/properties.c
index f732ec0..28d8b2f 100644
--- a/libcutils/properties.c
+++ b/libcutils/properties.c
@@ -52,19 +52,28 @@
return len;
}
-int property_list(void (*propfn)(const char *key, const char *value, void *cookie),
- void *cookie)
+struct property_list_callback_data
+{
+ void (*propfn)(const char *key, const char *value, void *cookie);
+ void *cookie;
+};
+
+static void property_list_callback(const prop_info *pi, void *cookie)
{
char name[PROP_NAME_MAX];
char value[PROP_VALUE_MAX];
- const prop_info *pi;
- unsigned n;
-
- for(n = 0; (pi = __system_property_find_nth(n)); n++) {
- __system_property_read(pi, name, value);
- propfn(name, value, cookie);
- }
- return 0;
+ struct property_list_callback_data *data = cookie;
+
+ __system_property_read(pi, name, value);
+ data->propfn(name, value, data->cookie);
+}
+
+int property_list(
+ void (*propfn)(const char *key, const char *value, void *cookie),
+ void *cookie)
+{
+ struct property_list_callback_data data = { propfn, cookie };
+ return __system_property_foreach(property_list_callback, &data);
}
#elif defined(HAVE_SYSTEM_PROPERTY_SERVER)
diff --git a/libcutils/tests/Android.mk b/libcutils/tests/Android.mk
new file mode 100644
index 0000000..6571161
--- /dev/null
+++ b/libcutils/tests/Android.mk
@@ -0,0 +1 @@
+include $(all-subdir-makefiles)
diff --git a/libcutils/tests/memset_mips/Android.mk b/libcutils/tests/memset_mips/Android.mk
new file mode 100644
index 0000000..c22fca9
--- /dev/null
+++ b/libcutils/tests/memset_mips/Android.mk
@@ -0,0 +1,23 @@
+# Copyright 2012 The Android Open Source Project
+
+ifeq ($(TARGET_ARCH),mips)
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ test_memset.c \
+ android_memset_dumb.S \
+ android_memset_test.S \
+ memset_cmips.S \
+ memset_omips.S
+
+LOCAL_MODULE:= test_memset
+
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_STATIC_LIBRARIES := libcutils libc
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_EXECUTABLE)
+
+endif
diff --git a/libcutils/tests/memset_mips/android_memset_dumb.S b/libcutils/tests/memset_mips/android_memset_dumb.S
new file mode 100644
index 0000000..c8a1a37
--- /dev/null
+++ b/libcutils/tests/memset_mips/android_memset_dumb.S
@@ -0,0 +1,36 @@
+ .global android_memset16_dumb
+ .type android_memset16_dumb, @function
+android_memset16_dumb:
+ .ent android_memset16_dumb
+
+ .set noreorder
+ beqz $a2,9f
+ srl $a2,1
+
+1: sh $a1,($a0)
+ subu $a2,1
+ bnez $a2,1b
+ addu $a0,2
+ .set reorder
+
+9: j $ra
+ .end android_memset16_dumb
+ .size android_memset16_dumb,.-android_memset16_dumb
+
+ .global android_memset32_dumb
+ .type android_memset32_dumb, @function
+android_memset32_dumb:
+ .ent android_memset32_dumb
+ .set noreorder
+ beqz $a2,9f
+ srl $a2,2
+
+1: sw $a1,($a0)
+ subu $a2,1
+ bnez $a2,1b
+ addu $a0,4
+ .set reorder
+
+9: j $ra
+ .end android_memset32_dumb
+ .size android_memset32_dumb,.-android_memset32_dumb
diff --git a/libcutils/tests/memset_mips/android_memset_test.S b/libcutils/tests/memset_mips/android_memset_test.S
new file mode 100644
index 0000000..e918843
--- /dev/null
+++ b/libcutils/tests/memset_mips/android_memset_test.S
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2006 The android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifdef NDEBUG
+#define DBG #
+#else
+#define DBG
+#endif
+
+ .text
+ .align
+
+ /*
+ * Optimized memset16 for MIPS
+ *
+ * void android_memset16_test(uint16_t* dst, uint16_t value, size_t size);
+ *
+ */
+
+ .global android_memset16_test
+ .type android_memset16_test, @function
+android_memset16_test:
+ .ent android_memset16_test
+ .set noreorder
+
+ /* Check parameters */
+DBG andi $t0,$a0,1 /* $a0 must be halfword aligned */
+DBG tne $t0
+DBG lui $t1,0xffff /* $a1 must be 16bits */
+DBG and $t1,$a1
+DBG tne $t1
+DBG andi $t2,$a2,1 /* $a2 must be even */
+DBG tne $t2
+
+#if (__mips==32) && (__mips_isa_rev>=2)
+ ins $a2,$0,0,1
+#else
+ li $t0,~1
+ and $a2,$t0
+#endif
+
+ move $t8,$ra
+ blez $a2,9f /* Anything to do? */
+ andi $t0,$a0,2 /* Check dst alignment */
+ /* Expand value to 32 bits and check destination alignment */
+#if (__mips==32) && (__mips_isa_rev>=2)
+ beqz $t0,.Laligned32 /* dst is 32 bit aligned */
+ ins $a1,$a1,16,16
+#else
+ sll $t2,$a1,16
+ beqz $t0,.Laligned32 /* dst is 32 bit aligned */
+ or $a1,$t2
+#endif
+ sh $a1,($a0) /* do one halfword to get aligned */
+ subu $a2,2
+ addu $a0,2
+
+.Laligned32:
+ and $t1,$a2,63 /* is there enough left to do a full 64 byte loop? */
+ beq $a2,$t1,1f
+ subu $t2,$a2,$t1 /* $t2 is the number of bytes to do in loop64 */
+ addu $t3,$a0,$t2 /* $t3 is the end marker for loop64 */
+ subu $a2,$t2
+.Lloop64:
+ addu $a0,64
+ sw $a1,-64($a0)
+ sw $a1,-60($a0)
+ sw $a1,-56($a0)
+ sw $a1,-52($a0)
+ sw $a1,-48($a0)
+ sw $a1,-44($a0)
+ sw $a1,-40($a0)
+ sw $a1,-36($a0)
+ sw $a1,-32($a0)
+ sw $a1,-28($a0)
+ sw $a1,-24($a0)
+ sw $a1,-20($a0)
+ sw $a1,-16($a0)
+ sw $a1,-12($a0)
+ sw $a1,-8($a0)
+ bne $a0,$t3,.Lloop64
+ sw $a1,-4($a0)
+
+ /* Do the last 0..62 bytes */
+1: li $t0,64+12
+ andi $t1,$a2,0x3c /* $t1 how many bytes to store using sw */
+ bal 1f
+ subu $t0,$t1 /* 64+12-$t0 is offset to jump from 1f */
+1: addu $ra,$t0
+ j $ra
+ subu $a2,$t1
+2: sw $a1,60($a0)
+ sw $a1,56($a0)
+ sw $a1,52($a0)
+ sw $a1,48($a0)
+ sw $a1,44($a0)
+ sw $a1,40($a0)
+ sw $a1,36($a0)
+ sw $a1,32($a0)
+ sw $a1,28($a0)
+ sw $a1,24($a0)
+ sw $a1,20($a0)
+ sw $a1,16($a0)
+ sw $a1,12($a0)
+ sw $a1,8($a0)
+ sw $a1,4($a0)
+ sw $a1,0($a0)
+
+ beqz $a2,9f
+ addu $a0,$t1
+ sh $a1,($a0)
+
+9: j $t8
+ nop
+ .end android_memset16_test
+ .size android_memset16_test,.-android_memset16_test
+
+ /*
+ * Optimized memset32 for MIPS
+ *
+ * void android_memset32_test(uint32_t* dst, uint32_t value, size_t size);
+ *
+ */
+ .global android_memset32_test
+ .type android_memset32_test, @function
+android_memset32_test:
+ .ent android_memset32_test
+ .set noreorder
+
+ /* Check parameters */
+DBG andi $t0,$a0,3 /* $a0 must be word aligned */
+DBG tne $t0
+DBG andi $t2,$a2,3 /* $a2 must be a multiple of 4 bytes */
+DBG tne $t2
+
+ b .Laligned32
+ move $t8,$ra
+ .end android_memset32_test
+ .size android_memset32_test,.-android_memset32_test
diff --git a/libcutils/tests/memset_mips/memset_cmips.S b/libcutils/tests/memset_mips/memset_cmips.S
new file mode 100644
index 0000000..f8f3a91
--- /dev/null
+++ b/libcutils/tests/memset_mips/memset_cmips.S
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2009
+ * MIPS Technologies, Inc., California.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of the MIPS Technologies, 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 MIPS TECHNOLOGIES, INC. ``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 MIPS TECHNOLOGIES, INC. 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.
+ */
+
+/************************************************************************
+ *
+ * memset.S, version "64h" with 1 cache line horizon for "pref 30" and 14 nops
+ * Version: "043009"
+ *
+ ************************************************************************/
+
+
+/************************************************************************
+ * Include files
+ ************************************************************************/
+
+#include "machine/asm.h"
+
+/*
+ * This routine could be optimized for MIPS64. The current code only
+ * uses MIPS32 instructions.
+ */
+
+#if defined(__MIPSEB__)
+# define SWHI swl /* high part is left in big-endian */
+#endif
+
+#if defined(__MIPSEL__)
+# define SWHI swr /* high part is right in little-endian */
+#endif
+
+#if !(defined(XGPROF) || defined(XPROF))
+#undef SETUP_GP
+#define SETUP_GP
+#endif
+
+LEAF(memset_cmips,0)
+
+ .set noreorder
+ .set noat
+
+ addu t0,a0,a2 # t0 is the "past the end" address
+ slti AT,a2,4 # is a2 less than 4?
+ bne AT,zero,.Llast4 # if yes, go to last4
+ move v0,a0 # memset returns the dst pointer
+
+ beq a1,zero,.Lset0
+ subu v1,zero,a0
+
+ # smear byte into 32 bit word
+#if (__mips==32) && (__mips_isa_rev>=2)
+ ins a1, a1, 8, 8 # Replicate fill byte into half-word.
+ ins a1, a1, 16, 16 # Replicate fill byte into word.
+#else
+ and a1,0xff
+ sll AT,a1,8
+ or a1,AT
+ sll AT,a1,16
+ or a1,AT
+#endif
+
+.Lset0: andi v1,v1,0x3 # word-unaligned address?
+ beq v1,zero,.Laligned # v1 is the unalignment count
+ subu a2,a2,v1
+ SWHI a1,0(a0)
+ addu a0,a0,v1
+
+# Here we have the "word-aligned" a0 (until the "last4")
+.Laligned:
+ andi t8,a2,0x3f # any 64-byte chunks?
+ # t8 is the byte count past 64-byte chunks
+ beq a2,t8,.Lchk8w # when a2==t8, no 64-byte chunks
+ # There will be at most 1 32-byte chunk then
+ subu a3,a2,t8 # subtract from a2 the reminder
+ # Here a3 counts bytes in 16w chunks
+ addu a3,a0,a3 # Now a3 is the final dst after 64-byte chunks
+
+# Find out, if there are any 64-byte chunks after which will be still at least
+# 96 bytes left. The value "96" is calculated as needed buffer for
+# "pref 30,64(a0)" prefetch, which can be used as "pref 30,0(a0)" after
+# incrementing "a0" by 64.
+# For "a2" below 160 there will be no such "pref 30 safe" 64-byte chunk.
+#
+ sltiu v1,a2,160
+ bgtz v1,.Lloop16w_nopref30 # skip "pref 30,0(a0)"
+ subu t7,a2,96 # subtract "pref 30 unsafe" region
+ # below we have at least 1 64-byte chunk which is "pref 30 safe"
+ andi t6,t7,0x3f # t6 is past "64-byte safe chunks" reminder
+ subu t5,t7,t6 # subtract from t7 the reminder
+ # Here t5 counts bytes in 16w "safe" chunks
+ addu t4,a0,t5 # Now t4 is the dst after 64-byte "safe" chunks
+
+# Don't use "pref 30,0(a0)" for a0 in a "middle" of a cache line
+# pref 30,0(a0)
+# Here we are in the region, where it is safe to use "pref 30,64(a0)"
+.Lloop16w:
+ addiu a0,a0,64
+ pref 30,-32(a0) # continue setting up the dest, addr 64-32
+ sw a1,-64(a0)
+ sw a1,-60(a0)
+ sw a1,-56(a0)
+ sw a1,-52(a0)
+ sw a1,-48(a0)
+ sw a1,-44(a0)
+ sw a1,-40(a0)
+ sw a1,-36(a0)
+ nop
+ nop # the extra nop instructions help to balance
+ nop # cycles needed for "store" + "fill" + "evict"
+ nop # For 64byte store there are needed 8 fill
+ nop # and 8 evict cycles, i.e. at least 32 instr.
+ nop
+ nop
+ pref 30,0(a0) # continue setting up the dest, addr 64-0
+ sw a1,-32(a0)
+ sw a1,-28(a0)
+ sw a1,-24(a0)
+ sw a1,-20(a0)
+ sw a1,-16(a0)
+ sw a1,-12(a0)
+ sw a1,-8(a0)
+ sw a1,-4(a0)
+ nop
+ nop
+ nop
+ nop # NOTE: adding 14 nop-s instead of 12 nop-s
+ nop # gives better results for "fast" memory
+ nop
+ bne a0,t4,.Lloop16w
+ nop
+
+ beq a0,a3,.Lchk8w # maybe no more 64-byte chunks?
+ nop # this "delayed slot" is useless ...
+
+.Lloop16w_nopref30: # there could be up to 3 "64-byte nopref30" chunks
+ addiu a0,a0,64
+ sw a1,-64(a0)
+ sw a1,-60(a0)
+ sw a1,-56(a0)
+ sw a1,-52(a0)
+ sw a1,-48(a0)
+ sw a1,-44(a0)
+ sw a1,-40(a0)
+ sw a1,-36(a0)
+ sw a1,-32(a0)
+ sw a1,-28(a0)
+ sw a1,-24(a0)
+ sw a1,-20(a0)
+ sw a1,-16(a0)
+ sw a1,-12(a0)
+ sw a1,-8(a0)
+ bne a0,a3,.Lloop16w_nopref30
+ sw a1,-4(a0)
+
+.Lchk8w: # t8 here is the byte count past 64-byte chunks
+
+ andi t7,t8,0x1f # is there a 32-byte chunk?
+ # the t7 is the reminder count past 32-bytes
+ beq t8,t7,.Lchk1w # when t8==t7, no 32-byte chunk
+ move a2,t7
+
+ sw a1,0(a0)
+ sw a1,4(a0)
+ sw a1,8(a0)
+ sw a1,12(a0)
+ sw a1,16(a0)
+ sw a1,20(a0)
+ sw a1,24(a0)
+ sw a1,28(a0)
+ addiu a0,a0,32
+
+.Lchk1w:
+ andi t8,a2,0x3 # now t8 is the reminder past 1w chunks
+ beq a2,t8,.Llast4
+ subu a3,a2,t8 # a3 is the count of bytes in 1w chunks
+ addu a3,a0,a3 # now a3 is the dst address past the 1w chunks
+
+# copying in words (4-byte chunks)
+.LwordCopy_loop:
+ addiu a0,a0,4
+ bne a0,a3,.LwordCopy_loop
+ sw a1,-4(a0)
+
+.Llast4:beq a0,t0,.Llast4e
+.Llast4l:addiu a0,a0,1
+ bne a0,t0,.Llast4l
+ sb a1,-1(a0)
+
+.Llast4e:
+ j ra
+ nop
+
+ .set at
+ .set reorder
+
+END(memset_cmips)
+
+
+/************************************************************************
+ * Implementation : Static functions
+ ************************************************************************/
+
diff --git a/libcutils/tests/memset_mips/memset_omips.S b/libcutils/tests/memset_mips/memset_omips.S
new file mode 100644
index 0000000..4c47001
--- /dev/null
+++ b/libcutils/tests/memset_mips/memset_omips.S
@@ -0,0 +1,90 @@
+/* Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Hartvig Ekner <hartvige@mips.com>, 2002.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+/* void *memset_omips(void *s, int c, size_t n). */
+
+#include "machine/asm.h"
+
+#ifdef __mips64
+#error mips32 code being compiled for mips64!
+#endif
+
+#if defined(__MIPSEB__)
+#error big-endian is not supported in Broadcom MIPS Android platform
+# define SWHI swl /* high part is left in big-endian */
+#else
+# define SWHI swr /* high part is right in little-endian */
+#endif
+
+LEAF (memset_omips,0)
+ .set noreorder
+
+ slti t1, a2, 8 # Less than 8?
+ bne t1, zero, .Llast8
+ move v0, a0 # Setup exit value before too late
+
+ beq a1, zero, .Lueven # If zero pattern, no need to extend
+ andi a1, 0xff # Avoid problems with bogus arguments
+ sll t0, a1, 8
+ or a1, t0
+ sll t0, a1, 16
+ or a1, t0 # a1 is now pattern in full word
+
+.Lueven:
+ subu t0, zero, a0 # Unaligned address?
+ andi t0, 0x3
+ beq t0, zero, .Lchkw
+ subu a2, t0
+ SWHI a1, 0(a0) # Yes, handle first unaligned part
+ addu a0, t0 # Now both a0 and a2 are updated
+
+.Lchkw:
+ andi t0, a2, 0x7 # Enough left for one loop iteration?
+ beq t0, a2, .Lchkl
+ subu a3, a2, t0
+ addu a3, a0 # a3 is last loop address +1
+ move a2, t0 # a2 is now # of bytes left after loop
+.Lloopw:
+ addiu a0, 8 # Handle 2 words pr. iteration
+ sw a1, -8(a0)
+ bne a0, a3, .Lloopw
+ sw a1, -4(a0)
+
+.Lchkl:
+ andi t0, a2, 0x4 # Check if there is at least a full
+ beq t0, zero, .Llast8 # word remaining after the loop
+ subu a2, t0
+ sw a1, 0(a0) # Yes...
+ addiu a0, 4
+
+.Llast8:
+ blez a2, .Lexit # Handle last 8 bytes (if cnt>0)
+ addu a3, a2, a0 # a3 is last address +1
+.Llst8l:
+ addiu a0, 1
+ bne a0, a3, .Llst8l
+ sb a1, -1(a0)
+.Lexit:
+ j ra # Bye, bye
+ nop
+
+ .set reorder
+END (memset_omips)
+
+
diff --git a/libcutils/tests/memset_mips/test_memset.c b/libcutils/tests/memset_mips/test_memset.c
new file mode 100644
index 0000000..9705c65
--- /dev/null
+++ b/libcutils/tests/memset_mips/test_memset.c
@@ -0,0 +1,235 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <cutils/memory.h>
+#include <time.h>
+
+/*
+ * All systems must implement or emulate the rdhwr instruction to read
+ * the userlocal register. Systems that emulate also return teh count register
+ * when accessing register $2 so this should work on most systems
+ */
+#define USE_RDHWR
+
+#ifdef USE_RDHWR
+#define UNITS "cycles"
+#define SCALE 2 /* Most CPU's */
+static inline uint32_t
+get_count(void)
+{
+ uint32_t res;
+ asm volatile (".set push; .set mips32r2; rdhwr %[res],$2; .set pop" : [res] "=r" (res) : : "memory");
+ return res;
+}
+#else
+#define UNITS "ns"
+#define SCALE 1
+static inline uint32_t
+get_count(void)
+{
+ struct timespec now;
+ uint32_t res;
+ clock_gettime(CLOCK_REALTIME, &now);
+ res = (uint32_t)(now.tv_sec * 1000000000LL + now.tv_nsec);
+ // printf ("now=%d.%09d res=%d\n", (int)now.tv_sec, (int)now.tv_nsec, res);
+ return res;
+}
+#endif
+
+uint32_t overhead;
+void
+measure_overhead(void)
+{
+ int i;
+ uint32_t start, stop, delta;
+ for (i = 0; i < 32; i++) {
+ start = get_count();
+ stop = get_count();
+ delta = stop - start;
+ if (overhead == 0 || delta < overhead)
+ overhead = delta;
+ }
+ printf("overhead is %d"UNITS"\n", overhead);
+}
+
+uint32_t
+timeone(void (*fn)(), void *d, uint32_t val, uint32_t bytes)
+{
+ uint32_t start, stop, delta;
+ start = get_count();
+ (*fn)(d, val, bytes);
+ stop = get_count();
+ delta = stop - start - overhead;
+ // printf ("start=0x%08x stop=0x%08x delta=0x%08x\n", start, stop, delta);
+ return delta * SCALE;
+}
+
+/* define VERIFY to check that memset only touches the bytes it's supposed to */
+/*#define VERIFY*/
+
+/*
+ * Using a big arena means that memset will most likely miss in the cache
+ * NB Enabling verification effectively warms up the cache...
+ */
+#define ARENASIZE 0x1000000
+#ifdef VERIFY
+char arena[ARENASIZE+8]; /* Allow space for guard words */
+#else
+char arena[ARENASIZE];
+#endif
+
+void
+testone(char *tag, void (*fn)(), int trials, int minbytes, int maxbytes, int size, int threshold)
+{
+ int offset;
+ void *d;
+ void *p;
+ uint32_t v, notv = 0;
+ uint32_t n;
+ int i, units;
+ int totalunits = 0, totalbytes = 0, samples = 0;
+
+ /* Reset RNG to ensure each test uses same random values */
+ srand(0); /* FIXME should be able to use some other seed than 0 */
+
+ for (i = 0; i < trials; i++) {
+ n = minbytes + (rand() % (maxbytes-minbytes)); /* How many bytes to do */
+ offset = ((rand() % (ARENASIZE-n))); /* Where to start */
+
+#ifdef VERIFY
+ offset += 4; /* Allow space for guard word at beginning */
+#endif
+ v = rand();
+
+ /* Adjust alignment and sizes based on transfer size */
+ switch (size) {
+ case 1:
+ v &= 0xff;
+ notv = ~v & 0xff;
+ break;
+ case 2:
+ v &= 0xffff;
+ notv = ~v & 0xffff;
+ offset &= ~1;
+ n &= ~1;
+ break;
+ case 4:
+ notv = ~v;
+ offset &= ~3;
+ n &= ~3;
+ break;
+ }
+
+ d = &arena[offset];
+
+#ifdef VERIFY
+ /* Initialise the area and guard words */
+ for (p = &arena[offset-4]; p < (void *)&arena[offset+n+4]; p = (void *)((uint32_t)p + size)) {
+ if (size == 1)
+ *(uint8_t *)p = notv;
+ else if (size == 2)
+ *(uint16_t *)p = notv;
+ else if (size == 4)
+ *(uint32_t *)p = notv;
+ }
+#endif
+ units = timeone(fn, d, v, n);
+#ifdef VERIFY
+ /* Check the area and guard words */
+ for (p = &arena[offset-4]; p < (void *)&arena[offset+n+4]; p = (void *)((uint32_t)p + size)) {
+ uint32_t got = 0;
+ if (size == 1)
+ got = *(uint8_t *)p;
+ else if (size == 2)
+ got = *(uint16_t *)p;
+ else if (size == 4)
+ got = *(uint32_t *)p;
+ if (p < (void *)&arena[offset]) {
+ if (got != notv)
+ printf ("%s: verify failure: preguard:%p d=%p v=%08x got=%08x n=%d\n", tag, p, d, v, got, n);
+ }
+ else if (p < (void *)&arena[offset+n]) {
+ if (got != v)
+ printf ("%s: verify failure: arena:%p d=%p v=%08x got=%08x n=%d\n", tag, p, d, v, n);
+ }
+ else {
+ if (got != notv)
+ printf ("%s: verify failure: postguard:%p d=%p v=%08x got=%08x n=%d\n", tag, p, d, v, n);
+ }
+ }
+#endif
+
+ /* If the cycle count looks reasonable include it in the statistics */
+ if (units < threshold) {
+ totalbytes += n;
+ totalunits += units;
+ samples++;
+ }
+ }
+
+ printf("%s: samples=%d avglen=%d avg" UNITS "=%d bp"UNITS"=%g\n",
+ tag, samples, totalbytes/samples, totalunits/samples, (double)totalbytes/(double)totalunits);
+}
+
+extern void android_memset32_dumb(uint32_t* dst, uint32_t value, size_t size);
+extern void android_memset16_dumb(uint32_t* dst, uint16_t value, size_t size);
+extern void android_memset32_test(uint32_t* dst, uint32_t value, size_t size);
+extern void android_memset16_test(uint32_t* dst, uint16_t value, size_t size);
+extern void memset_cmips(void* dst, int value, size_t size);
+extern void memset_omips(void* dst, int value, size_t size);
+
+int
+main(int argc, char **argv)
+{
+ int i;
+ struct {
+ char *type;
+ int trials;
+ int minbytes, maxbytes;
+ } *pp, params[] = {
+ {"small", 10000, 0, 64},
+ {"medium", 10000, 64, 512},
+ {"large", 10000, 512, 1280},
+ {"varied", 10000, 0, 1280},
+ };
+#define NPARAMS (sizeof(params)/sizeof(params[0]))
+ struct {
+ char *name;
+ void (*fn)();
+ int size;
+ } *fp, functions[] = {
+ {"dmemset16", (void (*)())android_memset16_dumb, 2},
+ {"tmemset16", (void (*)())android_memset16_test, 2},
+ {"lmemset16", (void (*)())android_memset16, 2},
+
+ {"dmemset32", (void (*)())android_memset32_dumb, 4},
+ {"tmemset32", (void (*)())android_memset32_test, 4},
+ {"lmemset32", (void (*)())android_memset32, 4},
+
+ {"cmemset", (void (*)())memset_cmips, 1},
+ {"omemset", (void (*)())memset_omips, 1},
+ {"lmemset", (void (*)())memset, 1},
+ };
+#define NFUNCTIONS (sizeof(functions)/sizeof(functions[0]))
+ char tag[40];
+ int threshold;
+
+ measure_overhead();
+
+ /* Warm up the page cache */
+ memset(arena, 0xff, ARENASIZE); /* use 0xff now to avoid COW later */
+
+ for (fp = functions; fp < &functions[NFUNCTIONS]; fp++) {
+ (fp->fn)(arena, 0xffffffff, ARENASIZE); /* one call to get the code into Icache */
+ for (pp = params; pp < ¶ms[NPARAMS]; pp++) {
+ sprintf(tag, "%10s: %7s %4d-%4d", fp->name, pp->type, pp->minbytes, pp->maxbytes);
+
+ /* Set the cycle threshold */
+ threshold = pp->maxbytes * 4 * 10; /* reasonable for cycles and ns */
+ testone(tag, fp->fn, pp->trials, pp->minbytes, pp->maxbytes, fp->size, threshold);
+ }
+ printf ("\n");
+ }
+
+ return 0;
+}
diff --git a/libcutils/tzfile.h b/libcutils/tzfile.h
deleted file mode 100644
index 8c70375..0000000
--- a/libcutils/tzfile.h
+++ /dev/null
@@ -1,180 +0,0 @@
-#ifndef TZFILE_H
-
-#define TZFILE_H
-
-/*
-** This file is in the public domain, so clarified as of
-** 1996-06-05 by Arthur David Olson.
-*/
-
-/*
-** This header is for use ONLY with the time conversion code.
-** There is no guarantee that it will remain unchanged,
-** or that it will remain at all.
-** Do NOT copy it to any system include directory.
-** Thank you!
-*/
-
-/*
-** ID
-*/
-
-#ifndef lint
-#ifndef NOID
-static char tzfilehid[] = "@(#)tzfile.h 8.1";
-#endif /* !defined NOID */
-#endif /* !defined lint */
-
-/*
-** Information about time zone files.
-*/
-
-#ifndef TZDIR
-#define TZDIR "/usr/share/zoneinfo" /* "/android/usr/share/zoneinfo" */ /* Time zone object file directory */
-#endif /* !defined TZDIR */
-
-#ifndef TZDEFAULT
-#define TZDEFAULT "localtime"
-#endif /* !defined TZDEFAULT */
-
-#ifndef TZDEFRULES
-#define TZDEFRULES "posixrules"
-#endif /* !defined TZDEFRULES */
-
-/*
-** Each file begins with. . .
-*/
-
-#define TZ_MAGIC "TZif"
-
-struct tzhead {
- char tzh_magic[4]; /* TZ_MAGIC */
- char tzh_version[1]; /* '\0' or '2' as of 2005 */
- char tzh_reserved[15]; /* reserved--must be zero */
- char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */
- char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
- char tzh_leapcnt[4]; /* coded number of leap seconds */
- char tzh_timecnt[4]; /* coded number of transition times */
- char tzh_typecnt[4]; /* coded number of local time types */
- char tzh_charcnt[4]; /* coded number of abbr. chars */
-};
-
-/*
-** . . .followed by. . .
-**
-** tzh_timecnt (char [4])s coded transition times a la time(2)
-** tzh_timecnt (unsigned char)s types of local time starting at above
-** tzh_typecnt repetitions of
-** one (char [4]) coded UTC offset in seconds
-** one (unsigned char) used to set tm_isdst
-** one (unsigned char) that's an abbreviation list index
-** tzh_charcnt (char)s '\0'-terminated zone abbreviations
-** tzh_leapcnt repetitions of
-** one (char [4]) coded leap second transition times
-** one (char [4]) total correction after above
-** tzh_ttisstdcnt (char)s indexed by type; if TRUE, transition
-** time is standard time, if FALSE,
-** transition time is wall clock time
-** if absent, transition times are
-** assumed to be wall clock time
-** tzh_ttisgmtcnt (char)s indexed by type; if TRUE, transition
-** time is UTC, if FALSE,
-** transition time is local time
-** if absent, transition times are
-** assumed to be local time
-*/
-
-/*
-** If tzh_version is '2' or greater, the above is followed by a second instance
-** of tzhead and a second instance of the data in which each coded transition
-** time uses 8 rather than 4 chars,
-** then a POSIX-TZ-environment-variable-style string for use in handling
-** instants after the last transition time stored in the file
-** (with nothing between the newlines if there is no POSIX representation for
-** such instants).
-*/
-
-/*
-** In the current implementation, "tzset()" refuses to deal with files that
-** exceed any of the limits below.
-*/
-
-#ifndef TZ_MAX_TIMES
-#define TZ_MAX_TIMES 1200
-#endif /* !defined TZ_MAX_TIMES */
-
-#ifndef TZ_MAX_TYPES
-#ifndef NOSOLAR
-#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
-#endif /* !defined NOSOLAR */
-#ifdef NOSOLAR
-/*
-** Must be at least 14 for Europe/Riga as of Jan 12 1995,
-** as noted by Earl Chew.
-*/
-#define TZ_MAX_TYPES 20 /* Maximum number of local time types */
-#endif /* !defined NOSOLAR */
-#endif /* !defined TZ_MAX_TYPES */
-
-#ifndef TZ_MAX_CHARS
-#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
- /* (limited by what unsigned chars can hold) */
-#endif /* !defined TZ_MAX_CHARS */
-
-#ifndef TZ_MAX_LEAPS
-#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
-#endif /* !defined TZ_MAX_LEAPS */
-
-#define SECSPERMIN 60
-#define MINSPERHOUR 60
-#define HOURSPERDAY 24
-#define DAYSPERWEEK 7
-#define DAYSPERNYEAR 365
-#define DAYSPERLYEAR 366
-#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
-#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY)
-#define MONSPERYEAR 12
-
-#define TM_SUNDAY 0
-#define TM_MONDAY 1
-#define TM_TUESDAY 2
-#define TM_WEDNESDAY 3
-#define TM_THURSDAY 4
-#define TM_FRIDAY 5
-#define TM_SATURDAY 6
-
-#define TM_JANUARY 0
-#define TM_FEBRUARY 1
-#define TM_MARCH 2
-#define TM_APRIL 3
-#define TM_MAY 4
-#define TM_JUNE 5
-#define TM_JULY 6
-#define TM_AUGUST 7
-#define TM_SEPTEMBER 8
-#define TM_OCTOBER 9
-#define TM_NOVEMBER 10
-#define TM_DECEMBER 11
-
-#define TM_YEAR_BASE 1900
-
-#define EPOCH_YEAR 1970
-#define EPOCH_WDAY TM_THURSDAY
-
-#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
-
-/*
-** Since everything in isleap is modulo 400 (or a factor of 400), we know that
-** isleap(y) == isleap(y % 400)
-** and so
-** isleap(a + b) == isleap((a + b) % 400)
-** or
-** isleap(a + b) == isleap(a % 400 + b % 400)
-** This is true even if % means modulo rather than Fortran remainder
-** (which is allowed by C89 but not C99).
-** We use this to avoid addition overflow problems.
-*/
-
-#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400)
-
-#endif /* !defined TZFILE_H */
diff --git a/libcutils/tzstrftime.c b/libcutils/tzstrftime.c
deleted file mode 100644
index e4f54df..0000000
--- a/libcutils/tzstrftime.c
+++ /dev/null
@@ -1,842 +0,0 @@
-#ifndef lint
-#ifndef NOID
-static char elsieid[] = "@(#)strftime.c 8.1";
-/*
-** Based on the UCB version with the ID appearing below.
-** This is ANSIish only when "multibyte character == plain character".
-*/
-#endif /* !defined NOID */
-#endif /* !defined lint */
-
-#include <stdio.h>
-#include <time.h>
-#include <tzfile.h>
-#include <limits.h>
-#include <cutils/tztime.h>
-
-/*
-** Copyright (c) 1989 The Regents of the University of California.
-** All rights reserved.
-**
-** Redistribution and use in source and binary forms are permitted
-** provided that the above copyright notice and this paragraph are
-** duplicated in all such forms and that any documentation,
-** advertising materials, and other materials related to such
-** distribution and use acknowledge that the software was developed
-** by the University of California, Berkeley. The name of the
-** University may not be used to endorse or promote products derived
-** from this software without specific prior written permission.
-** THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
-** IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
-** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
-*/
-
-#ifndef LIBC_SCCS
-#ifndef lint
-static const char sccsid[] = "@(#)strftime.c 5.4 (Berkeley) 3/14/89";
-#endif /* !defined lint */
-#endif /* !defined LIBC_SCCS */
-
-#include <ctype.h>
-
-#define P(x) x
-
-static char * _add P((const char *, char *, const char *, int));
-static char * _conv P((int, const char *, char *, const char *));
-static char * _fmt P((const char *, const struct tm *, char *, const char *,
- int *, const struct strftime_locale *Locale));
-static char * _yconv P((int, int, int, int, char *, const char *, int));
-static char * getformat P((int, char *, char *, char *, char *));
-
-extern char * tzname[];
-
-
-
-
-
-/* from private.h */
-
-#ifndef TYPE_BIT
-#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT)
-#endif /* !defined TYPE_BIT */
-
-#ifndef TYPE_SIGNED
-#define TYPE_SIGNED(type) (((type) -1) < 0)
-#endif /* !defined TYPE_SIGNED */
-
-#ifndef INT_STRLEN_MAXIMUM
-/*
- * ** 302 / 1000 is log10(2.0) rounded up.
- * ** Subtract one for the sign bit if the type is signed;
- * ** add one for integer division truncation;
- * ** add one more for a minus sign if the type is signed.
- * */
-#define INT_STRLEN_MAXIMUM(type) \
- ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + \
- 1 + TYPE_SIGNED(type))
-#endif /* !defined INT_STRLEN_MAXIMUM */
-
-/* end of part from private.h */
-
-
-
-
-#ifndef YEAR_2000_NAME
-#define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS"
-#endif /* !defined YEAR_2000_NAME */
-
-#define IN_NONE 0
-#define IN_SOME 1
-#define IN_THIS 2
-#define IN_ALL 3
-
-#define FORCE_LOWER_CASE 0x100
-
-size_t
-strftime_tz(s, maxsize, format, t, Locale)
-char * const s;
-const size_t maxsize;
-const char * const format;
-const struct tm * const t;
-const struct strftime_locale *Locale;
-{
- char * p;
- int warn;
-
- warn = IN_NONE;
- p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn, Locale);
-#if 0
- if (warn != IN_NONE && getenv(YEAR_2000_NAME) != NULL) {
- (void) fprintf(stderr, "\n");
- if (format == NULL)
- (void) fprintf(stderr, "NULL strftime format ");
- else (void) fprintf(stderr, "strftime format \"%s\" ",
- format);
- (void) fprintf(stderr, "yields only two digits of years in ");
- if (warn == IN_SOME)
- (void) fprintf(stderr, "some locales");
- else if (warn == IN_THIS)
- (void) fprintf(stderr, "the current locale");
- else (void) fprintf(stderr, "all locales");
- (void) fprintf(stderr, "\n");
- }
-#endif /* !defined NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU */
- if (p == s + maxsize)
- return 0;
- *p = '\0';
- return p - s;
-}
-
-static char *getformat(int modifier, char *normal, char *underscore,
- char *dash, char *zero) {
- switch (modifier) {
- case '_':
- return underscore;
-
- case '-':
- return dash;
-
- case '0':
- return zero;
- }
-
- return normal;
-}
-
-static char *
-_fmt(format, t, pt, ptlim, warnp, Locale)
-const char * format;
-const struct tm * const t;
-char * pt;
-const char * const ptlim;
-int * warnp;
-const struct strftime_locale *Locale;
-{
- for ( ; *format; ++format) {
- if (*format == '%') {
- int modifier = 0;
-label:
- switch (*++format) {
- case '\0':
- --format;
- break;
- case 'A':
- pt = _add((t->tm_wday < 0 ||
- t->tm_wday >= DAYSPERWEEK) ?
- "?" : Locale->weekday[t->tm_wday],
- pt, ptlim, modifier);
- continue;
- case 'a':
- pt = _add((t->tm_wday < 0 ||
- t->tm_wday >= DAYSPERWEEK) ?
- "?" : Locale->wday[t->tm_wday],
- pt, ptlim, modifier);
- continue;
- case 'B':
- if (modifier == '-') {
- pt = _add((t->tm_mon < 0 ||
- t->tm_mon >= MONSPERYEAR) ?
- "?" : Locale->standalone_month[t->tm_mon],
- pt, ptlim, modifier);
- } else {
- pt = _add((t->tm_mon < 0 ||
- t->tm_mon >= MONSPERYEAR) ?
- "?" : Locale->month[t->tm_mon],
- pt, ptlim, modifier);
- }
- continue;
- case 'b':
- case 'h':
- pt = _add((t->tm_mon < 0 ||
- t->tm_mon >= MONSPERYEAR) ?
- "?" : Locale->mon[t->tm_mon],
- pt, ptlim, modifier);
- continue;
- case 'C':
- /*
- ** %C used to do a...
- ** _fmt("%a %b %e %X %Y", t);
- ** ...whereas now POSIX 1003.2 calls for
- ** something completely different.
- ** (ado, 1993-05-24)
- */
- pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 0,
- pt, ptlim, modifier);
- continue;
- case 'c':
- {
- int warn2 = IN_SOME;
-
- pt = _fmt(Locale->c_fmt, t, pt, ptlim, warnp, Locale);
- if (warn2 == IN_ALL)
- warn2 = IN_THIS;
- if (warn2 > *warnp)
- *warnp = warn2;
- }
- continue;
- case 'D':
- pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp, Locale);
- continue;
- case 'd':
- pt = _conv(t->tm_mday,
- getformat(modifier, "%02d",
- "%2d", "%d", "%02d"),
- pt, ptlim);
- continue;
- case 'E':
- case 'O':
- /*
- ** C99 locale modifiers.
- ** The sequences
- ** %Ec %EC %Ex %EX %Ey %EY
- ** %Od %oe %OH %OI %Om %OM
- ** %OS %Ou %OU %OV %Ow %OW %Oy
- ** are supposed to provide alternate
- ** representations.
- */
- goto label;
- case '_':
- case '-':
- case '0':
- case '^':
- case '#':
- modifier = *format;
- goto label;
- case 'e':
- pt = _conv(t->tm_mday,
- getformat(modifier, "%2d",
- "%2d", "%d", "%02d"),
- pt, ptlim);
- continue;
- case 'F':
- pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp, Locale);
- continue;
- case 'H':
- pt = _conv(t->tm_hour,
- getformat(modifier, "%02d",
- "%2d", "%d", "%02d"),
- pt, ptlim);
- continue;
- case 'I':
- pt = _conv((t->tm_hour % 12) ?
- (t->tm_hour % 12) : 12,
- getformat(modifier, "%02d",
- "%2d", "%d", "%02d"),
- pt, ptlim);
- continue;
- case 'j':
- pt = _conv(t->tm_yday + 1,
- getformat(modifier, "%03d", "%3d", "%d", "%03d"),
- pt, ptlim);
- continue;
- case 'k':
- /*
- ** This used to be...
- ** _conv(t->tm_hour % 12 ?
- ** t->tm_hour % 12 : 12, 2, ' ');
- ** ...and has been changed to the below to
- ** match SunOS 4.1.1 and Arnold Robbins'
- ** strftime version 3.0. That is, "%k" and
- ** "%l" have been swapped.
- ** (ado, 1993-05-24)
- */
- pt = _conv(t->tm_hour,
- getformat(modifier, "%2d",
- "%2d", "%d", "%02d"),
- pt, ptlim);
- continue;
-#ifdef KITCHEN_SINK
- case 'K':
- /*
- ** After all this time, still unclaimed!
- */
- pt = _add("kitchen sink", pt, ptlim, modifier);
- continue;
-#endif /* defined KITCHEN_SINK */
- case 'l':
- /*
- ** This used to be...
- ** _conv(t->tm_hour, 2, ' ');
- ** ...and has been changed to the below to
- ** match SunOS 4.1.1 and Arnold Robbin's
- ** strftime version 3.0. That is, "%k" and
- ** "%l" have been swapped.
- ** (ado, 1993-05-24)
- */
- pt = _conv((t->tm_hour % 12) ?
- (t->tm_hour % 12) : 12,
- getformat(modifier, "%2d",
- "%2d", "%d", "%02d"),
- pt, ptlim);
- continue;
- case 'M':
- pt = _conv(t->tm_min,
- getformat(modifier, "%02d",
- "%2d", "%d", "%02d"),
- pt, ptlim);
- continue;
- case 'm':
- pt = _conv(t->tm_mon + 1,
- getformat(modifier, "%02d",
- "%2d", "%d", "%02d"),
- pt, ptlim);
- continue;
- case 'n':
- pt = _add("\n", pt, ptlim, modifier);
- continue;
- case 'p':
- pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
- Locale->pm :
- Locale->am,
- pt, ptlim, modifier);
- continue;
- case 'P':
- pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
- Locale->pm :
- Locale->am,
- pt, ptlim, FORCE_LOWER_CASE);
- continue;
- case 'R':
- pt = _fmt("%H:%M", t, pt, ptlim, warnp, Locale);
- continue;
- case 'r':
- pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp, Locale);
- continue;
- case 'S':
- pt = _conv(t->tm_sec,
- getformat(modifier, "%02d",
- "%2d", "%d", "%02d"),
- pt, ptlim);
- continue;
- case 's':
- {
- struct tm tm;
- char buf[INT_STRLEN_MAXIMUM(
- time_t) + 1];
- time_t mkt;
-
- tm = *t;
- mkt = mktime(&tm);
- if (TYPE_SIGNED(time_t))
- (void) sprintf(buf, "%ld",
- (long) mkt);
- else (void) sprintf(buf, "%lu",
- (unsigned long) mkt);
- pt = _add(buf, pt, ptlim, modifier);
- }
- continue;
- case 'T':
- pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp, Locale);
- continue;
- case 't':
- pt = _add("\t", pt, ptlim, modifier);
- continue;
- case 'U':
- pt = _conv((t->tm_yday + DAYSPERWEEK -
- t->tm_wday) / DAYSPERWEEK,
- getformat(modifier, "%02d",
- "%2d", "%d", "%02d"),
- pt, ptlim);
- continue;
- case 'u':
- /*
- ** From Arnold Robbins' strftime version 3.0:
- ** "ISO 8601: Weekday as a decimal number
- ** [1 (Monday) - 7]"
- ** (ado, 1993-05-24)
- */
- pt = _conv((t->tm_wday == 0) ?
- DAYSPERWEEK : t->tm_wday, "%d", pt, ptlim);
- continue;
- case 'V': /* ISO 8601 week number */
- case 'G': /* ISO 8601 year (four digits) */
- case 'g': /* ISO 8601 year (two digits) */
-/*
-** From Arnold Robbins' strftime version 3.0: "the week number of the
-** year (the first Monday as the first day of week 1) as a decimal number
-** (01-53)."
-** (ado, 1993-05-24)
-**
-** From "http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html" by Markus Kuhn:
-** "Week 01 of a year is per definition the first week which has the
-** Thursday in this year, which is equivalent to the week which contains
-** the fourth day of January. In other words, the first week of a new year
-** is the week which has the majority of its days in the new year. Week 01
-** might also contain days from the previous year and the week before week
-** 01 of a year is the last week (52 or 53) of the previous year even if
-** it contains days from the new year. A week starts with Monday (day 1)
-** and ends with Sunday (day 7). For example, the first week of the year
-** 1997 lasts from 1996-12-30 to 1997-01-05..."
-** (ado, 1996-01-02)
-*/
- {
- int year;
- int base;
- int yday;
- int wday;
- int w;
-
- year = t->tm_year;
- base = TM_YEAR_BASE;
- yday = t->tm_yday;
- wday = t->tm_wday;
- for ( ; ; ) {
- int len;
- int bot;
- int top;
-
- len = isleap_sum(year, base) ?
- DAYSPERLYEAR :
- DAYSPERNYEAR;
- /*
- ** What yday (-3 ... 3) does
- ** the ISO year begin on?
- */
- bot = ((yday + 11 - wday) %
- DAYSPERWEEK) - 3;
- /*
- ** What yday does the NEXT
- ** ISO year begin on?
- */
- top = bot -
- (len % DAYSPERWEEK);
- if (top < -3)
- top += DAYSPERWEEK;
- top += len;
- if (yday >= top) {
- ++base;
- w = 1;
- break;
- }
- if (yday >= bot) {
- w = 1 + ((yday - bot) /
- DAYSPERWEEK);
- break;
- }
- --base;
- yday += isleap_sum(year, base) ?
- DAYSPERLYEAR :
- DAYSPERNYEAR;
- }
-#ifdef XPG4_1994_04_09
- if ((w == 52 &&
- t->tm_mon == TM_JANUARY) ||
- (w == 1 &&
- t->tm_mon == TM_DECEMBER))
- w = 53;
-#endif /* defined XPG4_1994_04_09 */
- if (*format == 'V')
- pt = _conv(w,
- getformat(modifier,
- "%02d",
- "%2d",
- "%d",
- "%02d"),
- pt, ptlim);
- else if (*format == 'g') {
- *warnp = IN_ALL;
- pt = _yconv(year, base, 0, 1,
- pt, ptlim, modifier);
- } else pt = _yconv(year, base, 1, 1,
- pt, ptlim, modifier);
- }
- continue;
- case 'v':
- /*
- ** From Arnold Robbins' strftime version 3.0:
- ** "date as dd-bbb-YYYY"
- ** (ado, 1993-05-24)
- */
- pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp, Locale);
- continue;
- case 'W':
- pt = _conv((t->tm_yday + DAYSPERWEEK -
- (t->tm_wday ?
- (t->tm_wday - 1) :
- (DAYSPERWEEK - 1))) / DAYSPERWEEK,
- getformat(modifier, "%02d",
- "%2d", "%d", "%02d"),
- pt, ptlim);
- continue;
- case 'w':
- pt = _conv(t->tm_wday, "%d", pt, ptlim);
- continue;
- case 'X':
- pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp, Locale);
- continue;
- case 'x':
- {
- int warn2 = IN_SOME;
-
- pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2, Locale);
- if (warn2 == IN_ALL)
- warn2 = IN_THIS;
- if (warn2 > *warnp)
- *warnp = warn2;
- }
- continue;
- case 'y':
- *warnp = IN_ALL;
- pt = _yconv(t->tm_year, TM_YEAR_BASE, 0, 1,
- pt, ptlim, modifier);
- continue;
- case 'Y':
- pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 1,
- pt, ptlim, modifier);
- continue;
- case 'Z':
-#ifdef TM_ZONE
- if (t->TM_ZONE != NULL)
- pt = _add(t->TM_ZONE, pt, ptlim,
- modifier);
- else
-#endif /* defined TM_ZONE */
- if (t->tm_isdst >= 0)
- pt = _add(tzname[t->tm_isdst != 0],
- pt, ptlim, modifier);
- /*
- ** C99 says that %Z must be replaced by the
- ** empty string if the time zone is not
- ** determinable.
- */
- continue;
- case 'z':
- {
- int diff;
- char const * sign;
-
- if (t->tm_isdst < 0)
- continue;
-#ifdef TM_GMTOFF
- diff = t->TM_GMTOFF;
-#else /* !defined TM_GMTOFF */
- /*
- ** C99 says that the UTC offset must
- ** be computed by looking only at
- ** tm_isdst. This requirement is
- ** incorrect, since it means the code
- ** must rely on magic (in this case
- ** altzone and timezone), and the
- ** magic might not have the correct
- ** offset. Doing things correctly is
- ** tricky and requires disobeying C99;
- ** see GNU C strftime for details.
- ** For now, punt and conform to the
- ** standard, even though it's incorrect.
- **
- ** C99 says that %z must be replaced by the
- ** empty string if the time zone is not
- ** determinable, so output nothing if the
- ** appropriate variables are not available.
- */
- if (t->tm_isdst == 0)
-#ifdef USG_COMPAT
- diff = -timezone;
-#else /* !defined USG_COMPAT */
- continue;
-#endif /* !defined USG_COMPAT */
- else
-#ifdef ALTZONE
- diff = -altzone;
-#else /* !defined ALTZONE */
- continue;
-#endif /* !defined ALTZONE */
-#endif /* !defined TM_GMTOFF */
- if (diff < 0) {
- sign = "-";
- diff = -diff;
- } else sign = "+";
- pt = _add(sign, pt, ptlim, modifier);
- diff /= SECSPERMIN;
- diff = (diff / MINSPERHOUR) * 100 +
- (diff % MINSPERHOUR);
- pt = _conv(diff,
- getformat(modifier, "%04d",
- "%4d", "%d", "%04d"),
- pt, ptlim);
- }
- continue;
- case '+':
- pt = _fmt(Locale->date_fmt, t, pt, ptlim,
- warnp, Locale);
- continue;
- case '%':
- /*
- ** X311J/88-090 (4.12.3.5): if conversion char is
- ** undefined, behavior is undefined. Print out the
- ** character itself as printf(3) also does.
- */
- default:
- break;
- }
- }
- if (pt == ptlim)
- break;
- *pt++ = *format;
- }
- return pt;
-}
-
-static char *
-_conv(n, format, pt, ptlim)
-const int n;
-const char * const format;
-char * const pt;
-const char * const ptlim;
-{
- char buf[INT_STRLEN_MAXIMUM(int) + 1];
-
- (void) sprintf(buf, format, n);
- return _add(buf, pt, ptlim, 0);
-}
-
-static char *
-_add(str, pt, ptlim, modifier)
-const char * str;
-char * pt;
-const char * const ptlim;
-int modifier;
-{
- int c;
-
- switch (modifier) {
- case FORCE_LOWER_CASE:
- while (pt < ptlim && (*pt = tolower(*str++)) != '\0') {
- ++pt;
- }
- break;
-
- case '^':
- while (pt < ptlim && (*pt = toupper(*str++)) != '\0') {
- ++pt;
- }
- break;
-
- case '#':
- while (pt < ptlim && (c = *str++) != '\0') {
- if (isupper(c)) {
- c = tolower(c);
- } else if (islower(c)) {
- c = toupper(c);
- }
- *pt = c;
- ++pt;
- }
-
- break;
-
- default:
- while (pt < ptlim && (*pt = *str++) != '\0') {
- ++pt;
- }
- }
-
- return pt;
-}
-
-/*
-** POSIX and the C Standard are unclear or inconsistent about
-** what %C and %y do if the year is negative or exceeds 9999.
-** Use the convention that %C concatenated with %y yields the
-** same output as %Y, and that %Y contains at least 4 bytes,
-** with more only if necessary.
-*/
-
-static char *
-_yconv(a, b, convert_top, convert_yy, pt, ptlim, modifier)
-const int a;
-const int b;
-const int convert_top;
-const int convert_yy;
-char * pt;
-const char * const ptlim;
-int modifier;
-{
- register int lead;
- register int trail;
-
-#define DIVISOR 100
- trail = a % DIVISOR + b % DIVISOR;
- lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR;
- trail %= DIVISOR;
- if (trail < 0 && lead > 0) {
- trail += DIVISOR;
- --lead;
- } else if (lead < 0 && trail > 0) {
- trail -= DIVISOR;
- ++lead;
- }
- if (convert_top) {
- if (lead == 0 && trail < 0)
- pt = _add("-0", pt, ptlim, modifier);
- else pt = _conv(lead, getformat(modifier, "%02d",
- "%2d", "%d", "%02d"),
- pt, ptlim);
- }
- if (convert_yy)
- pt = _conv(((trail < 0) ? -trail : trail),
- getformat(modifier, "%02d", "%2d", "%d", "%02d"),
- pt, ptlim);
- return pt;
-}
-
-#ifdef LOCALE_HOME
-static struct lc_time_T *
-_loc P((void))
-{
- static const char locale_home[] = LOCALE_HOME;
- static const char lc_time[] = "LC_TIME";
- static char * locale_buf;
-
- int fd;
- int oldsun; /* "...ain't got nothin' to do..." */
- char * lbuf;
- char * name;
- char * p;
- const char ** ap;
- const char * plim;
- char filename[FILENAME_MAX];
- struct stat st;
- size_t namesize;
- size_t bufsize;
-
- /*
- ** Use localebuf.mon[0] to signal whether locale is already set up.
- */
- if (localebuf.mon[0])
- return &localebuf;
- name = setlocale(LC_TIME, (char *) NULL);
- if (name == NULL || *name == '\0')
- goto no_locale;
- /*
- ** If the locale name is the same as our cache, use the cache.
- */
- lbuf = locale_buf;
- if (lbuf != NULL && strcmp(name, lbuf) == 0) {
- p = lbuf;
- for (ap = (const char **) &localebuf;
- ap < (const char **) (&localebuf + 1);
- ++ap)
- *ap = p += strlen(p) + 1;
- return &localebuf;
- }
- /*
- ** Slurp the locale file into the cache.
- */
- namesize = strlen(name) + 1;
- if (sizeof filename <
- ((sizeof locale_home) + namesize + (sizeof lc_time)))
- goto no_locale;
- oldsun = 0;
- (void) sprintf(filename, "%s/%s/%s", locale_home, name, lc_time);
- fd = open(filename, O_RDONLY);
- if (fd < 0) {
- /*
- ** Old Sun systems have a different naming and data convention.
- */
- oldsun = 1;
- (void) sprintf(filename, "%s/%s/%s", locale_home,
- lc_time, name);
- fd = open(filename, O_RDONLY);
- if (fd < 0)
- goto no_locale;
- }
- if (fstat(fd, &st) != 0)
- goto bad_locale;
- if (st.st_size <= 0)
- goto bad_locale;
- bufsize = namesize + st.st_size;
- locale_buf = NULL;
- lbuf = (lbuf == NULL) ? malloc(bufsize) : realloc(lbuf, bufsize);
- if (lbuf == NULL)
- goto bad_locale;
- (void) strcpy(lbuf, name);
- p = lbuf + namesize;
- plim = p + st.st_size;
- if (read(fd, p, (size_t) st.st_size) != st.st_size)
- goto bad_lbuf;
- if (close(fd) != 0)
- goto bad_lbuf;
- /*
- ** Parse the locale file into localebuf.
- */
- if (plim[-1] != '\n')
- goto bad_lbuf;
- for (ap = (const char **) &localebuf;
- ap < (const char **) (&localebuf + 1);
- ++ap) {
- if (p == plim)
- goto bad_lbuf;
- *ap = p;
- while (*p != '\n')
- ++p;
- *p++ = '\0';
- }
- if (oldsun) {
- /*
- ** SunOS 4 used an obsolescent format; see localdtconv(3).
- ** c_fmt had the ``short format for dates and times together''
- ** (SunOS 4 date, "%a %b %e %T %Z %Y" in the C locale);
- ** date_fmt had the ``long format for dates''
- ** (SunOS 4 strftime %C, "%A, %B %e, %Y" in the C locale).
- ** Discard the latter in favor of the former.
- */
- localebuf.date_fmt = localebuf.c_fmt;
- }
- /*
- ** Record the successful parse in the cache.
- */
- locale_buf = lbuf;
-
- return &localebuf;
-
-bad_lbuf:
- free(lbuf);
-bad_locale:
- (void) close(fd);
-no_locale:
- localebuf = C_time_locale;
- locale_buf = NULL;
- return &localebuf;
-}
-#endif /* defined LOCALE_HOME */
diff --git a/libcutils/tztime.c b/libcutils/tztime.c
deleted file mode 100644
index d6448a1..0000000
--- a/libcutils/tztime.c
+++ /dev/null
@@ -1,1950 +0,0 @@
-/*
-** This file is in the public domain, so clarified as of
-** 1996-06-05 by Arthur David Olson.
-*/
-
-#include <stdio.h>
-
-#ifndef lint
-#ifndef NOID
-static char elsieid[] = "@(#)localtime.c 8.3";
-#endif /* !defined NOID */
-#endif /* !defined lint */
-
-/*
-** Leap second handling from Bradley White.
-** POSIX-style TZ environment variable handling from Guy Harris.
-*/
-
-/*LINTLIBRARY*/
-
-#include "private.h"
-#include "tzfile.h"
-#include "fcntl.h"
-#include "float.h" /* for FLT_MAX and DBL_MAX */
-
-#ifndef TZ_ABBR_MAX_LEN
-#define TZ_ABBR_MAX_LEN 16
-#endif /* !defined TZ_ABBR_MAX_LEN */
-
-#ifndef TZ_ABBR_CHAR_SET
-#define TZ_ABBR_CHAR_SET \
- "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 :+-._"
-#endif /* !defined TZ_ABBR_CHAR_SET */
-
-#ifndef TZ_ABBR_ERR_CHAR
-#define TZ_ABBR_ERR_CHAR '_'
-#endif /* !defined TZ_ABBR_ERR_CHAR */
-
-#define INDEXFILE "/system/usr/share/zoneinfo/zoneinfo.idx"
-#define DATAFILE "/system/usr/share/zoneinfo/zoneinfo.dat"
-#define NAMELEN 40
-#define INTLEN 4
-#define READLEN (NAMELEN + 3 * INTLEN)
-
-/*
-** SunOS 4.1.1 headers lack O_BINARY.
-*/
-
-#ifdef O_BINARY
-#define OPEN_MODE (O_RDONLY | O_BINARY)
-#endif /* defined O_BINARY */
-#ifndef O_BINARY
-#define OPEN_MODE O_RDONLY
-#endif /* !defined O_BINARY */
-
-/* Complex computations to determine the min/max of time_t depending
- * on TYPE_BIT / TYPE_SIGNED / TYPE_INTEGRAL.
- * These macros cannot be used in pre-processor directives, so we
- * let the C compiler do the work, which makes things a bit funky.
- */
-static const time_t TIME_T_MAX =
- TYPE_INTEGRAL(time_t) ?
- ( TYPE_SIGNED(time_t) ?
- ~((time_t)1 << (TYPE_BIT(time_t)-1))
- :
- ~(time_t)0
- )
- : /* if time_t is a floating point number */
- ( sizeof(time_t) > sizeof(float) ? (time_t)DBL_MAX : (time_t)FLT_MAX );
-
-static const time_t TIME_T_MIN =
- TYPE_INTEGRAL(time_t) ?
- ( TYPE_SIGNED(time_t) ?
- ((time_t)1 << (TYPE_BIT(time_t)-1))
- :
- 0
- )
- :
- ( sizeof(time_t) > sizeof(float) ? (time_t)DBL_MIN : (time_t)FLT_MIN );
-
-#ifndef WILDABBR
-/*
-** Someone might make incorrect use of a time zone abbreviation:
-** 1. They might reference tzname[0] before calling tzset (explicitly
-** or implicitly).
-** 2. They might reference tzname[1] before calling tzset (explicitly
-** or implicitly).
-** 3. They might reference tzname[1] after setting to a time zone
-** in which Daylight Saving Time is never observed.
-** 4. They might reference tzname[0] after setting to a time zone
-** in which Standard Time is never observed.
-** 5. They might reference tm.TM_ZONE after calling offtime.
-** What's best to do in the above cases is open to debate;
-** for now, we just set things up so that in any of the five cases
-** WILDABBR is used. Another possibility: initialize tzname[0] to the
-** string "tzname[0] used before set", and similarly for the other cases.
-** And another: initialize tzname[0] to "ERA", with an explanation in the
-** manual page of what this "time zone abbreviation" means (doing this so
-** that tzname[0] has the "normal" length of three characters).
-*/
-#define WILDABBR " "
-#endif /* !defined WILDABBR */
-
-static char wildabbr[] = WILDABBR;
-
-static const char gmt[] = "GMT";
-
-/*
-** The DST rules to use if TZ has no rules and we can't load TZDEFRULES.
-** We default to US rules as of 1999-08-17.
-** POSIX 1003.1 section 8.1.1 says that the default DST rules are
-** implementation dependent; for historical reasons, US rules are a
-** common default.
-*/
-#ifndef TZDEFRULESTRING
-#define TZDEFRULESTRING ",M4.1.0,M10.5.0"
-#endif /* !defined TZDEFDST */
-
-struct ttinfo { /* time type information */
- long tt_gmtoff; /* UTC offset in seconds */
- int tt_isdst; /* used to set tm_isdst */
- int tt_abbrind; /* abbreviation list index */
- int tt_ttisstd; /* TRUE if transition is std time */
- int tt_ttisgmt; /* TRUE if transition is UTC */
-};
-
-struct lsinfo { /* leap second information */
- time_t ls_trans; /* transition time */
- long ls_corr; /* correction to apply */
-};
-
-#define BIGGEST(a, b) (((a) > (b)) ? (a) : (b))
-
-#ifdef TZNAME_MAX
-#define MY_TZNAME_MAX TZNAME_MAX
-#endif /* defined TZNAME_MAX */
-#ifndef TZNAME_MAX
-#define MY_TZNAME_MAX 255
-#endif /* !defined TZNAME_MAX */
-
-struct state {
- int leapcnt;
- int timecnt;
- int typecnt;
- int charcnt;
- int goback;
- int goahead;
- time_t ats[TZ_MAX_TIMES];
- unsigned char types[TZ_MAX_TIMES];
- struct ttinfo ttis[TZ_MAX_TYPES];
- char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof gmt),
- (2 * (MY_TZNAME_MAX + 1)))];
- struct lsinfo lsis[TZ_MAX_LEAPS];
-};
-
-struct rule {
- int r_type; /* type of rule--see below */
- int r_day; /* day number of rule */
- int r_week; /* week number of rule */
- int r_mon; /* month number of rule */
- long r_time; /* transition time of rule */
-};
-
-#define JULIAN_DAY 0 /* Jn - Julian day */
-#define DAY_OF_YEAR 1 /* n - day of year */
-#define MONTH_NTH_DAY_OF_WEEK 2 /* Mm.n.d - month, week, day of week */
-
-/*
-** Prototypes for static functions.
-*/
-
-static long detzcode P((const char * codep));
-static time_t detzcode64 P((const char * codep));
-static int differ_by_repeat P((time_t t1, time_t t0));
-static const char * getzname P((const char * strp));
-static const char * getqzname P((const char * strp, const int delim));
-static const char * getnum P((const char * strp, int * nump, int min,
- int max));
-static const char * getsecs P((const char * strp, long * secsp));
-static const char * getoffset P((const char * strp, long * offsetp));
-static const char * getrule P((const char * strp, struct rule * rulep));
-static void gmtload P((struct state * sp));
-static struct tm * gmtsub P((const time_t * timep, long offset,
- struct tm * tmp));
-static struct tm * localsub P((const time_t * timep, long offset,
- struct tm * tmp, const struct state *sp));
-static int increment_overflow P((int * number, int delta));
-static int leaps_thru_end_of P((int y));
-static int long_increment_overflow P((long * number, int delta));
-static int long_normalize_overflow P((long * tensptr,
- int * unitsptr, int base));
-static int normalize_overflow P((int * tensptr, int * unitsptr,
- int base));
-static void settzname P((void));
-static time_t time1 P((struct tm * tmp,
- struct tm * (*funcp) P((const time_t *,
- long, struct tm *, const struct state* sp)),
- long offset, const struct state * sp));
-static time_t time2 P((struct tm *tmp,
- struct tm * (*funcp) P((const time_t *,
- long, struct tm*, const struct state* sp)),
- long offset, int * okayp, const struct state * sp));
-static time_t time2sub P((struct tm *tmp,
- struct tm * (*funcp) P((const time_t*, long, struct tm*,const struct state *sp)),
- long offset, int * okayp, int do_norm_secs,
- const struct state *sp));
-static struct tm * timesub P((const time_t * timep, long offset,
- const struct state * sp, struct tm * tmp));
-static int tmcomp P((const struct tm * atmp,
- const struct tm * btmp));
-static time_t transtime P((time_t janfirst, int year,
- const struct rule * rulep, long offset));
-static int tzload P((const char * name, struct state * sp,
- int doextend));
-static int tzload_uncached P((const char * name, struct state * sp,
- int doextend));
-static int tzparse P((const char * name, struct state * sp,
- int lastditch));
-
-#ifdef ALL_STATE
-static struct state * gmtptr;
-#endif /* defined ALL_STATE */
-
-#ifndef ALL_STATE
-static struct state gmtmem;
-#define gmtptr (&gmtmem)
-#endif /* State Farm */
-
-#define CACHE_COUNT 4
-static char * g_cacheNames[CACHE_COUNT] = {0,0};
-static struct state g_cacheStates[CACHE_COUNT];
-static int g_lastCache = 0;
-static struct state g_utc;
-unsigned char g_utcSet = 0;
-
-
-#ifndef TZ_STRLEN_MAX
-#define TZ_STRLEN_MAX 255
-#endif /* !defined TZ_STRLEN_MAX */
-
-static char lcl_TZname[TZ_STRLEN_MAX + 1];
-static int lcl_is_set;
-static int gmt_is_set;
-
-char * tzname[2] = {
- wildabbr,
- wildabbr
-};
-
-/*
-** Section 4.12.3 of X3.159-1989 requires that
-** Except for the strftime function, these functions [asctime,
-** ctime, gmtime, localtime] return values in one of two static
-** objects: a broken-down time structure and an array of char.
-** Thanks to Paul Eggert for noting this.
-*/
-
-static struct tm tm;
-
-#ifdef USG_COMPAT
-time_t timezone = 0;
-int daylight = 0;
-#endif /* defined USG_COMPAT */
-
-#ifdef ALTZONE
-time_t altzone = 0;
-#endif /* defined ALTZONE */
-
-static long
-detzcode(codep)
-const char * const codep;
-{
- register long result;
- register int i;
-
- result = (codep[0] & 0x80) ? ~0L : 0;
- for (i = 0; i < 4; ++i)
- result = (result << 8) | (codep[i] & 0xff);
- return result;
-}
-
-static time_t
-detzcode64(codep)
-const char * const codep;
-{
- register time_t result;
- register int i;
-
- result = (codep[0] & 0x80) ? (~(int_fast64_t) 0) : 0;
- for (i = 0; i < 8; ++i)
- result = result * 256 + (codep[i] & 0xff);
- return result;
-}
-
-static int
-differ_by_repeat(t1, t0)
-const time_t t1;
-const time_t t0;
-{
- if (TYPE_INTEGRAL(time_t) &&
- TYPE_BIT(time_t) - TYPE_SIGNED(time_t) < SECSPERREPEAT_BITS)
- return 0;
- return t1 - t0 == SECSPERREPEAT;
-}
-
-static int toint(unsigned char *s) {
- return (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
-}
-
-static int
-tzload(const char *name, struct state * const sp, const int doextend)
-{
- if (name) {
- int i, err;
- if (0 == strcmp(name, "UTC")) {
- if (!g_utcSet) {
- tzload_uncached(name, &g_utc, 1);
- g_utcSet = 1;
- }
- //printf("tzload: utc\n");
- *sp = g_utc;
- return 0;
- }
- for (i=0; i<CACHE_COUNT; i++) {
- if (g_cacheNames[i] && 0 == strcmp(name, g_cacheNames[i])) {
- *sp = g_cacheStates[i];
- //printf("tzload: hit: %s\n", name);
- return 0;
- }
- }
- //printf("tzload: miss: %s\n", name);
- g_lastCache++;
- if (g_lastCache >= CACHE_COUNT) {
- g_lastCache = 0;
- }
- i = g_lastCache;
- if (g_cacheNames[i]) {
- free(g_cacheNames[i]);
- }
- err = tzload_uncached(name, &(g_cacheStates[i]), 1);
- if (err == 0) {
- g_cacheNames[i] = strdup(name);
- *sp = g_cacheStates[i];
- return 0;
- } else {
- g_cacheNames[i] = NULL;
- return err;
- }
- }
- return tzload_uncached(name, sp, doextend);
-}
-
-static int
-tzload_uncached(name, sp, doextend)
-register const char * name;
-register struct state * const sp;
-register const int doextend;
-{
- register const char * p;
- register int i;
- register int fid;
- register int stored;
- register int nread;
- union {
- struct tzhead tzhead;
- char buf[2 * sizeof(struct tzhead) +
- 2 * sizeof *sp +
- 4 * TZ_MAX_TIMES];
- } u;
- int toread = sizeof u.buf;
-
- if (name == NULL && (name = TZDEFAULT) == NULL)
- return -1;
- {
- register int doaccess;
- /*
- ** Section 4.9.1 of the C standard says that
- ** "FILENAME_MAX expands to an integral constant expression
- ** that is the size needed for an array of char large enough
- ** to hold the longest file name string that the implementation
- ** guarantees can be opened."
- */
- char fullname[FILENAME_MAX + 1];
- const char *origname = name;
-
- if (name[0] == ':')
- ++name;
- doaccess = name[0] == '/';
- if (!doaccess) {
- if ((p = TZDIR) == NULL)
- return -1;
- if ((strlen(p) + strlen(name) + 1) >= sizeof fullname)
- return -1;
- (void) strcpy(fullname, p);
- (void) strcat(fullname, "/");
- (void) strcat(fullname, name);
- /*
- ** Set doaccess if '.' (as in "../") shows up in name.
- */
- if (strchr(name, '.') != NULL)
- doaccess = TRUE;
- name = fullname;
- }
- if (doaccess && access(name, R_OK) != 0)
- return -1;
- if ((fid = open(name, OPEN_MODE)) == -1) {
- char buf[READLEN];
- char name[NAMELEN + 1];
- int fidix = open(INDEXFILE, OPEN_MODE);
- int off = -1;
-
- if (fidix < 0) {
- return -1;
- }
-
- while (read(fidix, buf, sizeof(buf)) == sizeof(buf)) {
- memcpy(name, buf, NAMELEN);
- name[NAMELEN] = '\0';
-
- if (strcmp(name, origname) == 0) {
- off = toint((unsigned char *) buf + NAMELEN);
- toread = toint((unsigned char *) buf + NAMELEN + INTLEN);
- break;
- }
- }
-
- close(fidix);
-
- if (off < 0)
- return -1;
-
- fid = open(DATAFILE, OPEN_MODE);
-
- if (fid < 0) {
- return -1;
- }
-
- if (lseek(fid, off, SEEK_SET) < 0) {
- return -1;
- }
- }
- }
- nread = read(fid, u.buf, toread);
- if (close(fid) < 0 || nread <= 0)
- return -1;
- for (stored = 4; stored <= 8; stored *= 2) {
- int ttisstdcnt;
- int ttisgmtcnt;
-
- ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt);
- ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt);
- sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt);
- sp->timecnt = (int) detzcode(u.tzhead.tzh_timecnt);
- sp->typecnt = (int) detzcode(u.tzhead.tzh_typecnt);
- sp->charcnt = (int) detzcode(u.tzhead.tzh_charcnt);
- p = u.tzhead.tzh_charcnt + sizeof u.tzhead.tzh_charcnt;
- if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS ||
- sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES ||
- sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES ||
- sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS ||
- (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
- (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0))
- return -1;
- if (nread - (p - u.buf) <
- sp->timecnt * stored + /* ats */
- sp->timecnt + /* types */
- sp->typecnt * 6 + /* ttinfos */
- sp->charcnt + /* chars */
- sp->leapcnt * (stored + 4) + /* lsinfos */
- ttisstdcnt + /* ttisstds */
- ttisgmtcnt) /* ttisgmts */
- return -1;
- for (i = 0; i < sp->timecnt; ++i) {
- sp->ats[i] = (stored == 4) ?
- detzcode(p) : detzcode64(p);
- p += stored;
- }
- for (i = 0; i < sp->timecnt; ++i) {
- sp->types[i] = (unsigned char) *p++;
- if (sp->types[i] >= sp->typecnt)
- return -1;
- }
- for (i = 0; i < sp->typecnt; ++i) {
- register struct ttinfo * ttisp;
-
- ttisp = &sp->ttis[i];
- ttisp->tt_gmtoff = detzcode(p);
- p += 4;
- ttisp->tt_isdst = (unsigned char) *p++;
- if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1)
- return -1;
- ttisp->tt_abbrind = (unsigned char) *p++;
- if (ttisp->tt_abbrind < 0 ||
- ttisp->tt_abbrind > sp->charcnt)
- return -1;
- }
- for (i = 0; i < sp->charcnt; ++i)
- sp->chars[i] = *p++;
- sp->chars[i] = '\0'; /* ensure '\0' at end */
- for (i = 0; i < sp->leapcnt; ++i) {
- register struct lsinfo * lsisp;
-
- lsisp = &sp->lsis[i];
- lsisp->ls_trans = (stored == 4) ?
- detzcode(p) : detzcode64(p);
- p += stored;
- lsisp->ls_corr = detzcode(p);
- p += 4;
- }
- for (i = 0; i < sp->typecnt; ++i) {
- register struct ttinfo * ttisp;
-
- ttisp = &sp->ttis[i];
- if (ttisstdcnt == 0)
- ttisp->tt_ttisstd = FALSE;
- else {
- ttisp->tt_ttisstd = *p++;
- if (ttisp->tt_ttisstd != TRUE &&
- ttisp->tt_ttisstd != FALSE)
- return -1;
- }
- }
- for (i = 0; i < sp->typecnt; ++i) {
- register struct ttinfo * ttisp;
-
- ttisp = &sp->ttis[i];
- if (ttisgmtcnt == 0)
- ttisp->tt_ttisgmt = FALSE;
- else {
- ttisp->tt_ttisgmt = *p++;
- if (ttisp->tt_ttisgmt != TRUE &&
- ttisp->tt_ttisgmt != FALSE)
- return -1;
- }
- }
- /*
- ** Out-of-sort ats should mean we're running on a
- ** signed time_t system but using a data file with
- ** unsigned values (or vice versa).
- */
- for (i = 0; i < sp->timecnt - 2; ++i)
- if (sp->ats[i] > sp->ats[i + 1]) {
- ++i;
- if (TYPE_SIGNED(time_t)) {
- /*
- ** Ignore the end (easy).
- */
- sp->timecnt = i;
- } else {
- /*
- ** Ignore the beginning (harder).
- */
- register int j;
-
- for (j = 0; j + i < sp->timecnt; ++j) {
- sp->ats[j] = sp->ats[j + i];
- sp->types[j] = sp->types[j + i];
- }
- sp->timecnt = j;
- }
- break;
- }
- /*
- ** If this is an old file, we're done.
- */
- if (u.tzhead.tzh_version[0] == '\0')
- break;
- nread -= p - u.buf;
- for (i = 0; i < nread; ++i)
- u.buf[i] = p[i];
- /*
- ** If this is a narrow integer time_t system, we're done.
- */
- if (stored >= (int) sizeof(time_t) && TYPE_INTEGRAL(time_t))
- break;
- }
- if (doextend && nread > 2 &&
- u.buf[0] == '\n' && u.buf[nread - 1] == '\n' &&
- sp->typecnt + 2 <= TZ_MAX_TYPES) {
- struct state ts;
- register int result;
-
- u.buf[nread - 1] = '\0';
- result = tzparse(&u.buf[1], &ts, FALSE);
- if (result == 0 && ts.typecnt == 2 &&
- sp->charcnt + ts.charcnt <= TZ_MAX_CHARS) {
- for (i = 0; i < 2; ++i)
- ts.ttis[i].tt_abbrind +=
- sp->charcnt;
- for (i = 0; i < ts.charcnt; ++i)
- sp->chars[sp->charcnt++] =
- ts.chars[i];
- i = 0;
- while (i < ts.timecnt &&
- ts.ats[i] <=
- sp->ats[sp->timecnt - 1])
- ++i;
- while (i < ts.timecnt &&
- sp->timecnt < TZ_MAX_TIMES) {
- sp->ats[sp->timecnt] =
- ts.ats[i];
- sp->types[sp->timecnt] =
- sp->typecnt +
- ts.types[i];
- ++sp->timecnt;
- ++i;
- }
- sp->ttis[sp->typecnt++] = ts.ttis[0];
- sp->ttis[sp->typecnt++] = ts.ttis[1];
- }
- }
- i = 2 * YEARSPERREPEAT;
- sp->goback = sp->goahead = sp->timecnt > i;
- sp->goback &= sp->types[i] == sp->types[0] &&
- differ_by_repeat(sp->ats[i], sp->ats[0]);
- sp->goahead &=
- sp->types[sp->timecnt - 1] == sp->types[sp->timecnt - 1 - i] &&
- differ_by_repeat(sp->ats[sp->timecnt - 1],
- sp->ats[sp->timecnt - 1 - i]);
- return 0;
-}
-
-static const int mon_lengths[2][MONSPERYEAR] = {
- { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
- { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
-};
-
-static const int year_lengths[2] = {
- DAYSPERNYEAR, DAYSPERLYEAR
-};
-
-/*
-** Given a pointer into a time zone string, scan until a character that is not
-** a valid character in a zone name is found. Return a pointer to that
-** character.
-*/
-
-static const char *
-getzname(strp)
-register const char * strp;
-{
- register char c;
-
- while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' &&
- c != '+')
- ++strp;
- return strp;
-}
-
-/*
-** Given a pointer into an extended time zone string, scan until the ending
-** delimiter of the zone name is located. Return a pointer to the delimiter.
-**
-** As with getzname above, the legal character set is actually quite
-** restricted, with other characters producing undefined results.
-** We don't do any checking here; checking is done later in common-case code.
-*/
-
-static const char *
-getqzname(register const char *strp, const int delim)
-{
- register int c;
-
- while ((c = *strp) != '\0' && c != delim)
- ++strp;
- return strp;
-}
-
-/*
-** Given a pointer into a time zone string, extract a number from that string.
-** Check that the number is within a specified range; if it is not, return
-** NULL.
-** Otherwise, return a pointer to the first character not part of the number.
-*/
-
-static const char *
-getnum(strp, nump, min, max)
-register const char * strp;
-int * const nump;
-const int min;
-const int max;
-{
- register char c;
- register int num;
-
- if (strp == NULL || !is_digit(c = *strp))
- return NULL;
- num = 0;
- do {
- num = num * 10 + (c - '0');
- if (num > max)
- return NULL; /* illegal value */
- c = *++strp;
- } while (is_digit(c));
- if (num < min)
- return NULL; /* illegal value */
- *nump = num;
- return strp;
-}
-
-/*
-** Given a pointer into a time zone string, extract a number of seconds,
-** in hh[:mm[:ss]] form, from the string.
-** If any error occurs, return NULL.
-** Otherwise, return a pointer to the first character not part of the number
-** of seconds.
-*/
-
-static const char *
-getsecs(strp, secsp)
-register const char * strp;
-long * const secsp;
-{
- int num;
-
- /*
- ** `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like
- ** "M10.4.6/26", which does not conform to Posix,
- ** but which specifies the equivalent of
- ** ``02:00 on the first Sunday on or after 23 Oct''.
- */
- strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);
- if (strp == NULL)
- return NULL;
- *secsp = num * (long) SECSPERHOUR;
- if (*strp == ':') {
- ++strp;
- strp = getnum(strp, &num, 0, MINSPERHOUR - 1);
- if (strp == NULL)
- return NULL;
- *secsp += num * SECSPERMIN;
- if (*strp == ':') {
- ++strp;
- /* `SECSPERMIN' allows for leap seconds. */
- strp = getnum(strp, &num, 0, SECSPERMIN);
- if (strp == NULL)
- return NULL;
- *secsp += num;
- }
- }
- return strp;
-}
-
-/*
-** Given a pointer into a time zone string, extract an offset, in
-** [+-]hh[:mm[:ss]] form, from the string.
-** If any error occurs, return NULL.
-** Otherwise, return a pointer to the first character not part of the time.
-*/
-
-static const char *
-getoffset(strp, offsetp)
-register const char * strp;
-long * const offsetp;
-{
- register int neg = 0;
-
- if (*strp == '-') {
- neg = 1;
- ++strp;
- } else if (*strp == '+')
- ++strp;
- strp = getsecs(strp, offsetp);
- if (strp == NULL)
- return NULL; /* illegal time */
- if (neg)
- *offsetp = -*offsetp;
- return strp;
-}
-
-/*
-** Given a pointer into a time zone string, extract a rule in the form
-** date[/time]. See POSIX section 8 for the format of "date" and "time".
-** If a valid rule is not found, return NULL.
-** Otherwise, return a pointer to the first character not part of the rule.
-*/
-
-static const char *
-getrule(strp, rulep)
-const char * strp;
-register struct rule * const rulep;
-{
- if (*strp == 'J') {
- /*
- ** Julian day.
- */
- rulep->r_type = JULIAN_DAY;
- ++strp;
- strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR);
- } else if (*strp == 'M') {
- /*
- ** Month, week, day.
- */
- rulep->r_type = MONTH_NTH_DAY_OF_WEEK;
- ++strp;
- strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR);
- if (strp == NULL)
- return NULL;
- if (*strp++ != '.')
- return NULL;
- strp = getnum(strp, &rulep->r_week, 1, 5);
- if (strp == NULL)
- return NULL;
- if (*strp++ != '.')
- return NULL;
- strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1);
- } else if (is_digit(*strp)) {
- /*
- ** Day of year.
- */
- rulep->r_type = DAY_OF_YEAR;
- strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1);
- } else return NULL; /* invalid format */
- if (strp == NULL)
- return NULL;
- if (*strp == '/') {
- /*
- ** Time specified.
- */
- ++strp;
- strp = getsecs(strp, &rulep->r_time);
- } else rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */
- return strp;
-}
-
-/*
-** Given the Epoch-relative time of January 1, 00:00:00 UTC, in a year, the
-** year, a rule, and the offset from UTC at the time that rule takes effect,
-** calculate the Epoch-relative time that rule takes effect.
-*/
-
-static time_t
-transtime(janfirst, year, rulep, offset)
-const time_t janfirst;
-const int year;
-register const struct rule * const rulep;
-const long offset;
-{
- register int leapyear;
- register time_t value;
- register int i;
- int d, m1, yy0, yy1, yy2, dow;
-
- INITIALIZE(value);
- leapyear = isleap(year);
- switch (rulep->r_type) {
-
- case JULIAN_DAY:
- /*
- ** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap
- ** years.
- ** In non-leap years, or if the day number is 59 or less, just
- ** add SECSPERDAY times the day number-1 to the time of
- ** January 1, midnight, to get the day.
- */
- value = janfirst + (rulep->r_day - 1) * SECSPERDAY;
- if (leapyear && rulep->r_day >= 60)
- value += SECSPERDAY;
- break;
-
- case DAY_OF_YEAR:
- /*
- ** n - day of year.
- ** Just add SECSPERDAY times the day number to the time of
- ** January 1, midnight, to get the day.
- */
- value = janfirst + rulep->r_day * SECSPERDAY;
- break;
-
- case MONTH_NTH_DAY_OF_WEEK:
- /*
- ** Mm.n.d - nth "dth day" of month m.
- */
- value = janfirst;
- for (i = 0; i < rulep->r_mon - 1; ++i)
- value += mon_lengths[leapyear][i] * SECSPERDAY;
-
- /*
- ** Use Zeller's Congruence to get day-of-week of first day of
- ** month.
- */
- m1 = (rulep->r_mon + 9) % 12 + 1;
- yy0 = (rulep->r_mon <= 2) ? (year - 1) : year;
- yy1 = yy0 / 100;
- yy2 = yy0 % 100;
- dow = ((26 * m1 - 2) / 10 +
- 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
- if (dow < 0)
- dow += DAYSPERWEEK;
-
- /*
- ** "dow" is the day-of-week of the first day of the month. Get
- ** the day-of-month (zero-origin) of the first "dow" day of the
- ** month.
- */
- d = rulep->r_day - dow;
- if (d < 0)
- d += DAYSPERWEEK;
- for (i = 1; i < rulep->r_week; ++i) {
- if (d + DAYSPERWEEK >=
- mon_lengths[leapyear][rulep->r_mon - 1])
- break;
- d += DAYSPERWEEK;
- }
-
- /*
- ** "d" is the day-of-month (zero-origin) of the day we want.
- */
- value += d * SECSPERDAY;
- break;
- }
-
- /*
- ** "value" is the Epoch-relative time of 00:00:00 UTC on the day in
- ** question. To get the Epoch-relative time of the specified local
- ** time on that day, add the transition time and the current offset
- ** from UTC.
- */
- return value + rulep->r_time + offset;
-}
-
-/*
-** Given a POSIX section 8-style TZ string, fill in the rule tables as
-** appropriate.
-*/
-
-static int
-tzparse(name, sp, lastditch)
-const char * name;
-register struct state * const sp;
-const int lastditch;
-{
- const char * stdname;
- const char * dstname;
- size_t stdlen;
- size_t dstlen;
- long stdoffset;
- long dstoffset;
- register time_t * atp;
- register unsigned char * typep;
- register char * cp;
- register int load_result;
-
- INITIALIZE(dstname);
- stdname = name;
- if (lastditch) {
- stdlen = strlen(name); /* length of standard zone name */
- name += stdlen;
- if (stdlen >= sizeof sp->chars)
- stdlen = (sizeof sp->chars) - 1;
- stdoffset = 0;
- } else {
- if (*name == '<') {
- name++;
- stdname = name;
- name = getqzname(name, '>');
- if (*name != '>')
- return (-1);
- stdlen = name - stdname;
- name++;
- } else {
- name = getzname(name);
- stdlen = name - stdname;
- }
- if (*name == '\0')
- return -1;
- name = getoffset(name, &stdoffset);
- if (name == NULL)
- return -1;
- }
- load_result = tzload(TZDEFRULES, sp, FALSE);
- if (load_result != 0)
- sp->leapcnt = 0; /* so, we're off a little */
- sp->timecnt = 0;
- if (*name != '\0') {
- if (*name == '<') {
- dstname = ++name;
- name = getqzname(name, '>');
- if (*name != '>')
- return -1;
- dstlen = name - dstname;
- name++;
- } else {
- dstname = name;
- name = getzname(name);
- dstlen = name - dstname; /* length of DST zone name */
- }
- if (*name != '\0' && *name != ',' && *name != ';') {
- name = getoffset(name, &dstoffset);
- if (name == NULL)
- return -1;
- } else dstoffset = stdoffset - SECSPERHOUR;
- if (*name == '\0' && load_result != 0)
- name = TZDEFRULESTRING;
- if (*name == ',' || *name == ';') {
- struct rule start;
- struct rule end;
- register int year;
- register time_t janfirst;
- time_t starttime;
- time_t endtime;
-
- ++name;
- if ((name = getrule(name, &start)) == NULL)
- return -1;
- if (*name++ != ',')
- return -1;
- if ((name = getrule(name, &end)) == NULL)
- return -1;
- if (*name != '\0')
- return -1;
- sp->typecnt = 2; /* standard time and DST */
- /*
- ** Two transitions per year, from EPOCH_YEAR forward.
- */
- sp->ttis[0].tt_gmtoff = -dstoffset;
- sp->ttis[0].tt_isdst = 1;
- sp->ttis[0].tt_abbrind = stdlen + 1;
- sp->ttis[1].tt_gmtoff = -stdoffset;
- sp->ttis[1].tt_isdst = 0;
- sp->ttis[1].tt_abbrind = 0;
- atp = sp->ats;
- typep = sp->types;
- janfirst = 0;
- for (year = EPOCH_YEAR;
- sp->timecnt + 2 <= TZ_MAX_TIMES;
- ++year) {
- time_t newfirst;
-
- starttime = transtime(janfirst, year, &start,
- stdoffset);
- endtime = transtime(janfirst, year, &end,
- dstoffset);
- if (starttime > endtime) {
- *atp++ = endtime;
- *typep++ = 1; /* DST ends */
- *atp++ = starttime;
- *typep++ = 0; /* DST begins */
- } else {
- *atp++ = starttime;
- *typep++ = 0; /* DST begins */
- *atp++ = endtime;
- *typep++ = 1; /* DST ends */
- }
- sp->timecnt += 2;
- newfirst = janfirst;
- newfirst += year_lengths[isleap(year)] *
- SECSPERDAY;
- if (newfirst <= janfirst)
- break;
- janfirst = newfirst;
- }
- } else {
- register long theirstdoffset;
- register long theirdstoffset;
- register long theiroffset;
- register int isdst;
- register int i;
- register int j;
-
- if (*name != '\0')
- return -1;
- /*
- ** Initial values of theirstdoffset and theirdstoffset.
- */
- theirstdoffset = 0;
- for (i = 0; i < sp->timecnt; ++i) {
- j = sp->types[i];
- if (!sp->ttis[j].tt_isdst) {
- theirstdoffset =
- -sp->ttis[j].tt_gmtoff;
- break;
- }
- }
- theirdstoffset = 0;
- for (i = 0; i < sp->timecnt; ++i) {
- j = sp->types[i];
- if (sp->ttis[j].tt_isdst) {
- theirdstoffset =
- -sp->ttis[j].tt_gmtoff;
- break;
- }
- }
- /*
- ** Initially we're assumed to be in standard time.
- */
- isdst = FALSE;
- theiroffset = theirstdoffset;
- /*
- ** Now juggle transition times and types
- ** tracking offsets as you do.
- */
- for (i = 0; i < sp->timecnt; ++i) {
- j = sp->types[i];
- sp->types[i] = sp->ttis[j].tt_isdst;
- if (sp->ttis[j].tt_ttisgmt) {
- /* No adjustment to transition time */
- } else {
- /*
- ** If summer time is in effect, and the
- ** transition time was not specified as
- ** standard time, add the summer time
- ** offset to the transition time;
- ** otherwise, add the standard time
- ** offset to the transition time.
- */
- /*
- ** Transitions from DST to DDST
- ** will effectively disappear since
- ** POSIX provides for only one DST
- ** offset.
- */
- if (isdst && !sp->ttis[j].tt_ttisstd) {
- sp->ats[i] += dstoffset -
- theirdstoffset;
- } else {
- sp->ats[i] += stdoffset -
- theirstdoffset;
- }
- }
- theiroffset = -sp->ttis[j].tt_gmtoff;
- if (sp->ttis[j].tt_isdst)
- theirdstoffset = theiroffset;
- else theirstdoffset = theiroffset;
- }
- /*
- ** Finally, fill in ttis.
- ** ttisstd and ttisgmt need not be handled.
- */
- sp->ttis[0].tt_gmtoff = -stdoffset;
- sp->ttis[0].tt_isdst = FALSE;
- sp->ttis[0].tt_abbrind = 0;
- sp->ttis[1].tt_gmtoff = -dstoffset;
- sp->ttis[1].tt_isdst = TRUE;
- sp->ttis[1].tt_abbrind = stdlen + 1;
- sp->typecnt = 2;
- }
- } else {
- dstlen = 0;
- sp->typecnt = 1; /* only standard time */
- sp->timecnt = 0;
- sp->ttis[0].tt_gmtoff = -stdoffset;
- sp->ttis[0].tt_isdst = 0;
- sp->ttis[0].tt_abbrind = 0;
- }
- sp->charcnt = stdlen + 1;
- if (dstlen != 0)
- sp->charcnt += dstlen + 1;
- if ((size_t) sp->charcnt > sizeof sp->chars)
- return -1;
- cp = sp->chars;
- (void) strncpy(cp, stdname, stdlen);
- cp += stdlen;
- *cp++ = '\0';
- if (dstlen != 0) {
- (void) strncpy(cp, dstname, dstlen);
- *(cp + dstlen) = '\0';
- }
- return 0;
-}
-
-static void
-gmtload(sp)
-struct state * const sp;
-{
- if (tzload(gmt, sp, TRUE) != 0)
- (void) tzparse(gmt, sp, TRUE);
-}
-
-/*
-** The easy way to behave "as if no library function calls" localtime
-** is to not call it--so we drop its guts into "localsub", which can be
-** freely called. (And no, the PANS doesn't require the above behavior--
-** but it *is* desirable.)
-**
-** The unused offset argument is for the benefit of mktime variants.
-*/
-
-/*ARGSUSED*/
-static struct tm *
-localsub(timep, offset, tmp, sp)
-const time_t * const timep;
-const long offset;
-struct tm * const tmp;
-const struct state * sp;
-{
- register const struct ttinfo * ttisp;
- register int i;
- register struct tm * result;
- const time_t t = *timep;
-
-#ifdef ALL_STATE
- if (sp == NULL)
- return gmtsub(timep, offset, tmp);
-#endif /* defined ALL_STATE */
- if ((sp->goback && t < sp->ats[0]) ||
- (sp->goahead && t > sp->ats[sp->timecnt - 1])) {
- time_t newt = t;
- register time_t seconds;
- register time_t tcycles;
- register int_fast64_t icycles;
-
- if (t < sp->ats[0])
- seconds = sp->ats[0] - t;
- else seconds = t - sp->ats[sp->timecnt - 1];
- --seconds;
- tcycles = seconds / YEARSPERREPEAT / AVGSECSPERYEAR;
- ++tcycles;
- icycles = tcycles;
- if (tcycles - icycles >= 1 || icycles - tcycles >= 1)
- return NULL;
- seconds = icycles;
- seconds *= YEARSPERREPEAT;
- seconds *= AVGSECSPERYEAR;
- if (t < sp->ats[0])
- newt += seconds;
- else newt -= seconds;
- if (newt < sp->ats[0] ||
- newt > sp->ats[sp->timecnt - 1])
- return NULL; /* "cannot happen" */
- result = localsub(&newt, offset, tmp, sp);
- if (result == tmp) {
- register time_t newy;
-
- newy = tmp->tm_year;
- if (t < sp->ats[0])
- newy -= icycles * YEARSPERREPEAT;
- else newy += icycles * YEARSPERREPEAT;
- tmp->tm_year = newy;
- if (tmp->tm_year != newy)
- return NULL;
- }
- return result;
- }
- if (sp->timecnt == 0 || t < sp->ats[0]) {
- i = 0;
- while (sp->ttis[i].tt_isdst)
- if (++i >= sp->typecnt) {
- i = 0;
- break;
- }
- } else {
- register int lo = 1;
- register int hi = sp->timecnt;
-
- while (lo < hi) {
- register int mid = (lo + hi) >> 1;
-
- if (t < sp->ats[mid])
- hi = mid;
- else lo = mid + 1;
- }
- i = (int) sp->types[lo - 1];
- }
- ttisp = &sp->ttis[i];
- /*
- ** To get (wrong) behavior that's compatible with System V Release 2.0
- ** you'd replace the statement below with
- ** t += ttisp->tt_gmtoff;
- ** timesub(&t, 0L, sp, tmp);
- */
- result = timesub(&t, ttisp->tt_gmtoff, sp, tmp);
- tmp->tm_isdst = ttisp->tt_isdst;
-#ifdef HAVE_TM_GMTOFF
- tmp->tm_gmtoff = ttisp->tt_gmtoff;
-#endif
- tzname[tmp->tm_isdst] = &sp->chars[ttisp->tt_abbrind];
-#ifdef TM_ZONE
- tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind];
-#endif /* defined TM_ZONE */
- return result;
-}
-
-
-// ============================================================================
-#if 0
-struct tm *
-localtime(timep)
-const time_t * const timep;
-{
- tzset();
- return localsub(timep, 0L, &tm);
-}
-#endif
-
-/*
-** Re-entrant version of localtime.
-*/
-
-// ============================================================================
-void
-localtime_tz(const time_t * const timep, struct tm * tmp, const char* tz)
-{
- struct state st;
- if (tzload(tz, &st, TRUE) != 0) {
- // not sure what's best here, but for now, we fall back to gmt
- gmtload(&st);
- }
-
- localsub(timep, 0L, tmp, &st);
-}
-
-/*
-** gmtsub is to gmtime as localsub is to localtime.
-*/
-
-static struct tm *
-gmtsub(timep, offset, tmp)
-const time_t * const timep;
-const long offset;
-struct tm * const tmp;
-{
- register struct tm * result;
-
- if (!gmt_is_set) {
- gmt_is_set = TRUE;
-#ifdef ALL_STATE
- gmtptr = (struct state *) malloc(sizeof *gmtptr);
- if (gmtptr != NULL)
-#endif /* defined ALL_STATE */
- gmtload(gmtptr);
- }
- result = timesub(timep, offset, gmtptr, tmp);
-#ifdef TM_ZONE
- /*
- ** Could get fancy here and deliver something such as
- ** "UTC+xxxx" or "UTC-xxxx" if offset is non-zero,
- ** but this is no time for a treasure hunt.
- */
- if (offset != 0)
- tmp->TM_ZONE = wildabbr;
- else {
-#ifdef ALL_STATE
- if (gmtptr == NULL)
- tmp->TM_ZONE = gmt;
- else tmp->TM_ZONE = gmtptr->chars;
-#endif /* defined ALL_STATE */
-#ifndef ALL_STATE
- tmp->TM_ZONE = gmtptr->chars;
-#endif /* State Farm */
- }
-#endif /* defined TM_ZONE */
- return result;
-}
-
-// ============================================================================
-#if 0
-struct tm *
-gmtime(timep)
-const time_t * const timep;
-{
- return gmtsub(timep, 0L, &tm);
-}
-#endif
-
-/*
-* Re-entrant version of gmtime.
-*/
-
-// ============================================================================
-#if 0
-struct tm *
-gmtime_r(timep, tmp)
-const time_t * const timep;
-struct tm * tmp;
-{
- return gmtsub(timep, 0L, tmp);
-}
-#endif
-
-#ifdef STD_INSPIRED
-
-// ============================================================================
-#if 0
-struct tm *
-offtime(timep, offset)
-const time_t * const timep;
-const long offset;
-{
- return gmtsub(timep, offset, &tm);
-}
-#endif
-
-#endif /* defined STD_INSPIRED */
-
-/*
-** Return the number of leap years through the end of the given year
-** where, to make the math easy, the answer for year zero is defined as zero.
-*/
-
-static int
-leaps_thru_end_of(y)
-register const int y;
-{
- return (y >= 0) ? (y / 4 - y / 100 + y / 400) :
- -(leaps_thru_end_of(-(y + 1)) + 1);
-}
-
-static struct tm *
-timesub(timep, offset, sp, tmp)
-const time_t * const timep;
-const long offset;
-register const struct state * const sp;
-register struct tm * const tmp;
-{
- register const struct lsinfo * lp;
- register time_t tdays;
- register int idays; /* unsigned would be so 2003 */
- register long rem;
- int y;
- register const int * ip;
- register long corr;
- register int hit;
- register int i;
-
- corr = 0;
- hit = 0;
-#ifdef ALL_STATE
- i = (sp == NULL) ? 0 : sp->leapcnt;
-#endif /* defined ALL_STATE */
-#ifndef ALL_STATE
- i = sp->leapcnt;
-#endif /* State Farm */
- while (--i >= 0) {
- lp = &sp->lsis[i];
- if (*timep >= lp->ls_trans) {
- if (*timep == lp->ls_trans) {
- hit = ((i == 0 && lp->ls_corr > 0) ||
- lp->ls_corr > sp->lsis[i - 1].ls_corr);
- if (hit)
- while (i > 0 &&
- sp->lsis[i].ls_trans ==
- sp->lsis[i - 1].ls_trans + 1 &&
- sp->lsis[i].ls_corr ==
- sp->lsis[i - 1].ls_corr + 1) {
- ++hit;
- --i;
- }
- }
- corr = lp->ls_corr;
- break;
- }
- }
- y = EPOCH_YEAR;
- tdays = *timep / SECSPERDAY;
- rem = *timep - tdays * SECSPERDAY;
- while (tdays < 0 || tdays >= year_lengths[isleap(y)]) {
- int newy;
- register time_t tdelta;
- register int idelta;
- register int leapdays;
-
- tdelta = tdays / DAYSPERLYEAR;
- idelta = tdelta;
- if (tdelta - idelta >= 1 || idelta - tdelta >= 1)
- return NULL;
- if (idelta == 0)
- idelta = (tdays < 0) ? -1 : 1;
- newy = y;
- if (increment_overflow(&newy, idelta))
- return NULL;
- leapdays = leaps_thru_end_of(newy - 1) -
- leaps_thru_end_of(y - 1);
- tdays -= ((time_t) newy - y) * DAYSPERNYEAR;
- tdays -= leapdays;
- y = newy;
- }
- {
- register long seconds;
-
- seconds = tdays * SECSPERDAY + 0.5;
- tdays = seconds / SECSPERDAY;
- rem += seconds - tdays * SECSPERDAY;
- }
- /*
- ** Given the range, we can now fearlessly cast...
- */
- idays = tdays;
- rem += offset - corr;
- while (rem < 0) {
- rem += SECSPERDAY;
- --idays;
- }
- while (rem >= SECSPERDAY) {
- rem -= SECSPERDAY;
- ++idays;
- }
- while (idays < 0) {
- if (increment_overflow(&y, -1))
- return NULL;
- idays += year_lengths[isleap(y)];
- }
- while (idays >= year_lengths[isleap(y)]) {
- idays -= year_lengths[isleap(y)];
- if (increment_overflow(&y, 1))
- return NULL;
- }
- tmp->tm_year = y;
- if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE))
- return NULL;
- tmp->tm_yday = idays;
- /*
- ** The "extra" mods below avoid overflow problems.
- */
- tmp->tm_wday = EPOCH_WDAY +
- ((y - EPOCH_YEAR) % DAYSPERWEEK) *
- (DAYSPERNYEAR % DAYSPERWEEK) +
- leaps_thru_end_of(y - 1) -
- leaps_thru_end_of(EPOCH_YEAR - 1) +
- idays;
- tmp->tm_wday %= DAYSPERWEEK;
- if (tmp->tm_wday < 0)
- tmp->tm_wday += DAYSPERWEEK;
- tmp->tm_hour = (int) (rem / SECSPERHOUR);
- rem %= SECSPERHOUR;
- tmp->tm_min = (int) (rem / SECSPERMIN);
- /*
- ** A positive leap second requires a special
- ** representation. This uses "... ??:59:60" et seq.
- */
- tmp->tm_sec = (int) (rem % SECSPERMIN) + hit;
- ip = mon_lengths[isleap(y)];
- for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon))
- idays -= ip[tmp->tm_mon];
- tmp->tm_mday = (int) (idays + 1);
- tmp->tm_isdst = 0;
-#ifdef TM_GMTOFF
- tmp->TM_GMTOFF = offset;
-#endif /* defined TM_GMTOFF */
- return tmp;
-}
-
-// ============================================================================
-#if 0
-char *
-ctime(timep)
-const time_t * const timep;
-{
-/*
-** Section 4.12.3.2 of X3.159-1989 requires that
-** The ctime function converts the calendar time pointed to by timer
-** to local time in the form of a string. It is equivalent to
-** asctime(localtime(timer))
-*/
- return asctime(localtime(timep));
-}
-#endif
-
-// ============================================================================
-#if 0
-char *
-ctime_r(timep, buf)
-const time_t * const timep;
-char * buf;
-{
- struct tm mytm;
-
- return asctime_r(localtime_r(timep, &mytm), buf);
-}
-#endif
-
-/*
-** Adapted from code provided by Robert Elz, who writes:
-** The "best" way to do mktime I think is based on an idea of Bob
-** Kridle's (so its said...) from a long time ago.
-** It does a binary search of the time_t space. Since time_t's are
-** just 32 bits, its a max of 32 iterations (even at 64 bits it
-** would still be very reasonable).
-*/
-
-#ifndef WRONG
-#define WRONG (-1)
-#endif /* !defined WRONG */
-
-/*
-** Simplified normalize logic courtesy Paul Eggert.
-*/
-
-static int
-increment_overflow(number, delta)
-int * number;
-int delta;
-{
- unsigned number0 = (unsigned)*number;
- unsigned number1 = (unsigned)(number0 + delta);
-
- *number = (int)number1;
-
- if (delta >= 0) {
- return ((int)number1 < (int)number0);
- } else {
- return ((int)number1 > (int)number0);
- }
-}
-
-static int
-long_increment_overflow(number, delta)
-long * number;
-int delta;
-{
- unsigned long number0 = (unsigned long)*number;
- unsigned long number1 = (unsigned long)(number0 + delta);
-
- *number = (long)number1;
-
- if (delta >= 0) {
- return ((long)number1 < (long)number0);
- } else {
- return ((long)number1 > (long)number0);
- }
-}
-
-static int
-normalize_overflow(tensptr, unitsptr, base)
-int * const tensptr;
-int * const unitsptr;
-const int base;
-{
- register int tensdelta;
-
- tensdelta = (*unitsptr >= 0) ?
- (*unitsptr / base) :
- (-1 - (-1 - *unitsptr) / base);
- *unitsptr -= tensdelta * base;
- return increment_overflow(tensptr, tensdelta);
-}
-
-static int
-long_normalize_overflow(tensptr, unitsptr, base)
-long * const tensptr;
-int * const unitsptr;
-const int base;
-{
- register int tensdelta;
-
- tensdelta = (*unitsptr >= 0) ?
- (*unitsptr / base) :
- (-1 - (-1 - *unitsptr) / base);
- *unitsptr -= tensdelta * base;
- return long_increment_overflow(tensptr, tensdelta);
-}
-
-static int
-tmcomp(atmp, btmp)
-register const struct tm * const atmp;
-register const struct tm * const btmp;
-{
- register int result;
-
- if ((result = (atmp->tm_year - btmp->tm_year)) == 0 &&
- (result = (atmp->tm_mon - btmp->tm_mon)) == 0 &&
- (result = (atmp->tm_mday - btmp->tm_mday)) == 0 &&
- (result = (atmp->tm_hour - btmp->tm_hour)) == 0 &&
- (result = (atmp->tm_min - btmp->tm_min)) == 0)
- result = atmp->tm_sec - btmp->tm_sec;
- return result;
-}
-
-static time_t
-time2sub(tmp, funcp, offset, okayp, do_norm_secs, sp)
-struct tm * const tmp;
-struct tm * (* const funcp) P((const time_t*, long, struct tm*,const struct state *sp));
-const long offset;
-int * const okayp;
-const int do_norm_secs;
-const struct state * sp;
-{
- register int dir;
- register int i, j;
- register int saved_seconds;
- register long li;
- register time_t lo;
- register time_t hi;
- long y;
- time_t newt;
- time_t t;
- struct tm yourtm, mytm;
-
- *okayp = FALSE;
- yourtm = *tmp;
- if (do_norm_secs) {
- if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec,
- SECSPERMIN))
- return WRONG;
- }
- if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR))
- return WRONG;
- if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY))
- return WRONG;
- y = yourtm.tm_year;
- if (long_normalize_overflow(&y, &yourtm.tm_mon, MONSPERYEAR))
- return WRONG;
- /*
- ** Turn y into an actual year number for now.
- ** It is converted back to an offset from TM_YEAR_BASE later.
- */
- if (long_increment_overflow(&y, TM_YEAR_BASE))
- return WRONG;
- while (yourtm.tm_mday <= 0) {
- if (long_increment_overflow(&y, -1))
- return WRONG;
- li = y + (1 < yourtm.tm_mon);
- yourtm.tm_mday += year_lengths[isleap(li)];
- }
- while (yourtm.tm_mday > DAYSPERLYEAR) {
- li = y + (1 < yourtm.tm_mon);
- yourtm.tm_mday -= year_lengths[isleap(li)];
- if (long_increment_overflow(&y, 1))
- return WRONG;
- }
- for ( ; ; ) {
- i = mon_lengths[isleap(y)][yourtm.tm_mon];
- if (yourtm.tm_mday <= i)
- break;
- yourtm.tm_mday -= i;
- if (++yourtm.tm_mon >= MONSPERYEAR) {
- yourtm.tm_mon = 0;
- if (long_increment_overflow(&y, 1))
- return WRONG;
- }
- }
- if (long_increment_overflow(&y, -TM_YEAR_BASE))
- return WRONG;
- yourtm.tm_year = y;
- if (yourtm.tm_year != y)
- return WRONG;
- if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN)
- saved_seconds = 0;
- else if (y + TM_YEAR_BASE < EPOCH_YEAR) {
- /*
- ** We can't set tm_sec to 0, because that might push the
- ** time below the minimum representable time.
- ** Set tm_sec to 59 instead.
- ** This assumes that the minimum representable time is
- ** not in the same minute that a leap second was deleted from,
- ** which is a safer assumption than using 58 would be.
- */
- if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN))
- return WRONG;
- saved_seconds = yourtm.tm_sec;
- yourtm.tm_sec = SECSPERMIN - 1;
- } else {
- saved_seconds = yourtm.tm_sec;
- yourtm.tm_sec = 0;
- }
- /*
- ** Do a binary search (this works whatever time_t's type is).
- */
- if (!TYPE_SIGNED(time_t)) {
- lo = 0;
- hi = lo - 1;
- } else if (!TYPE_INTEGRAL(time_t)) {
- if (sizeof(time_t) > sizeof(float))
- hi = (time_t) DBL_MAX;
- else hi = (time_t) FLT_MAX;
- lo = -hi;
- } else {
- lo = 1;
- for (i = 0; i < (int) TYPE_BIT(time_t) - 1; ++i)
- lo *= 2;
- hi = -(lo + 1);
- }
- for ( ; ; ) {
- t = lo / 2 + hi / 2;
- if (t < lo)
- t = lo;
- else if (t > hi)
- t = hi;
- if ((*funcp)(&t, offset, &mytm, sp) == NULL) {
- /*
- ** Assume that t is too extreme to be represented in
- ** a struct tm; arrange things so that it is less
- ** extreme on the next pass.
- */
- dir = (t > 0) ? 1 : -1;
- } else dir = tmcomp(&mytm, &yourtm);
- if (dir != 0) {
- if (t == lo) {
- if (t == TIME_T_MAX)
- return WRONG;
- ++t;
- ++lo;
- } else if (t == hi) {
- if (t == TIME_T_MIN)
- return WRONG;
- --t;
- --hi;
- }
- if (lo > hi)
- return WRONG;
- if (dir > 0)
- hi = t;
- else lo = t;
- continue;
- }
- if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst)
- break;
- /*
- ** Right time, wrong type.
- ** Hunt for right time, right type.
- ** It's okay to guess wrong since the guess
- ** gets checked.
- */
- /*
- ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
- */
-#ifdef ALL_STATE
- if (sp == NULL)
- return WRONG;
-#endif /* defined ALL_STATE */
- for (i = sp->typecnt - 1; i >= 0; --i) {
- if (sp->ttis[i].tt_isdst != yourtm.tm_isdst)
- continue;
- for (j = sp->typecnt - 1; j >= 0; --j) {
- if (sp->ttis[j].tt_isdst == yourtm.tm_isdst)
- continue;
- newt = t + sp->ttis[j].tt_gmtoff -
- sp->ttis[i].tt_gmtoff;
- if ((*funcp)(&newt, offset, &mytm, sp) == NULL)
- continue;
- if (tmcomp(&mytm, &yourtm) != 0)
- continue;
- if (mytm.tm_isdst != yourtm.tm_isdst)
- continue;
- /*
- ** We have a match.
- */
- t = newt;
- goto label;
- }
- }
- return WRONG;
- }
-label:
- newt = t + saved_seconds;
- if ((newt < t) != (saved_seconds < 0))
- return WRONG;
- t = newt;
- if ((*funcp)(&t, offset, tmp, sp))
- *okayp = TRUE;
- return t;
-}
-
-static time_t
-time2(tmp, funcp, offset, okayp, sp)
-struct tm * const tmp;
-struct tm * (* const funcp) P((const time_t*, long, struct tm*,
- const struct state* sp));
-const long offset;
-int * const okayp;
-const struct state * sp;
-{
- time_t t;
-
- /*
- ** First try without normalization of seconds
- ** (in case tm_sec contains a value associated with a leap second).
- ** If that fails, try with normalization of seconds.
- */
- t = time2sub(tmp, funcp, offset, okayp, FALSE, sp);
- return *okayp ? t : time2sub(tmp, funcp, offset, okayp, TRUE, sp);
-}
-
-static time_t
-time1(tmp, funcp, offset, sp)
-struct tm * const tmp;
-struct tm * (* const funcp) P((const time_t *, long, struct tm *, const struct state* sp));
-const long offset;
-const struct state * sp;
-{
- register time_t t;
- register int samei, otheri;
- register int sameind, otherind;
- register int i;
- register int nseen;
- int seen[TZ_MAX_TYPES];
- int types[TZ_MAX_TYPES];
- int okay;
-
- if (tmp->tm_isdst > 1)
- tmp->tm_isdst = 1;
- t = time2(tmp, funcp, offset, &okay, sp);
-#define PCTS 1
-#ifdef PCTS
- /*
- ** PCTS code courtesy Grant Sullivan.
- */
- if (okay)
- return t;
- if (tmp->tm_isdst < 0)
- tmp->tm_isdst = 0; /* reset to std and try again */
-#endif /* defined PCTS */
-#ifndef PCTS
- if (okay || tmp->tm_isdst < 0)
- return t;
-#endif /* !defined PCTS */
- /*
- ** We're supposed to assume that somebody took a time of one type
- ** and did some math on it that yielded a "struct tm" that's bad.
- ** We try to divine the type they started from and adjust to the
- ** type they need.
- */
- /*
- ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
- */
-#ifdef ALL_STATE
- if (sp == NULL)
- return WRONG;
-#endif /* defined ALL_STATE */
- for (i = 0; i < sp->typecnt; ++i)
- seen[i] = FALSE;
- nseen = 0;
- for (i = sp->timecnt - 1; i >= 0; --i)
- if (!seen[sp->types[i]]) {
- seen[sp->types[i]] = TRUE;
- types[nseen++] = sp->types[i];
- }
- for (sameind = 0; sameind < nseen; ++sameind) {
- samei = types[sameind];
- if (sp->ttis[samei].tt_isdst != tmp->tm_isdst)
- continue;
- for (otherind = 0; otherind < nseen; ++otherind) {
- otheri = types[otherind];
- if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst)
- continue;
- tmp->tm_sec += sp->ttis[otheri].tt_gmtoff -
- sp->ttis[samei].tt_gmtoff;
- tmp->tm_isdst = !tmp->tm_isdst;
- t = time2(tmp, funcp, offset, &okay, sp);
- if (okay)
- return t;
- tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff -
- sp->ttis[samei].tt_gmtoff;
- tmp->tm_isdst = !tmp->tm_isdst;
- }
- }
- return WRONG;
-}
-
-// ============================================================================
-time_t
-mktime_tz(struct tm * const tmp, char const * tz)
-{
- struct state st;
- if (tzload(tz, &st, TRUE) != 0) {
- // not sure what's best here, but for now, we fall back to gmt
- gmtload(&st);
- }
- return time1(tmp, localsub, 0L, &st);
-}
diff --git a/liblog/logd_write.c b/liblog/logd_write.c
index d812abc..3613d25 100644
--- a/liblog/logd_write.c
+++ b/liblog/logd_write.c
@@ -24,6 +24,8 @@
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
#include <cutils/logger.h>
#include <cutils/logd.h>
@@ -37,7 +39,7 @@
#define log_writev(filedes, vector, count) fakeLogWritev(filedes, vector, count)
#define log_close(filedes) fakeLogClose(filedes)
#else
-#define log_open(pathname, flags) open(pathname, flags)
+#define log_open(pathname, flags) open(pathname, (flags) | O_CLOEXEC)
#define log_writev(filedes, vector, count) writev(filedes, vector, count)
#define log_close(filedes) close(filedes)
#endif
diff --git a/libnetutils/dhcp_utils.c b/libnetutils/dhcp_utils.c
index d0ca90a..b940453 100644
--- a/libnetutils/dhcp_utils.c
+++ b/libnetutils/dhcp_utils.c
@@ -211,7 +211,7 @@
p2p_interface, DHCP_CONFIG_PATH, prop_value, interface);
else
snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:-f %s %s", DAEMON_NAME,
- DHCP_CONFIG_PATH, p2p_interface, interface);
+ p2p_interface, DHCP_CONFIG_PATH, interface);
memset(prop_value, '\0', PROPERTY_VALUE_MAX);
property_set(ctrl_prop, daemon_cmd);
if (wait_for_property(daemon_prop_name, desired_status, 10) < 0) {
@@ -367,9 +367,8 @@
return -1;
}
if (strcmp(prop_value, "ok") == 0) {
- fill_ip_info(interface, ipaddr, gateway, prefixLength,
+ return fill_ip_info(interface, ipaddr, gateway, prefixLength,
dns1, dns2, server, lease, vendorInfo);
- return 0;
} else {
snprintf(errmsg, sizeof(errmsg), "DHCP Renew result was %s", prop_value);
return -1;
diff --git a/libsparse/output_file.c b/libsparse/output_file.c
index b5ae419..5014e4a 100644
--- a/libsparse/output_file.c
+++ b/libsparse/output_file.c
@@ -675,6 +675,9 @@
} else {
out = output_file_new_normal();
}
+ if (!out) {
+ return NULL;
+ }
out->ops->open(out, fd);
diff --git a/libsuspend/Android.mk b/libsuspend/Android.mk
index 45cb701..a2fa3e0 100644
--- a/libsuspend/Android.mk
+++ b/libsuspend/Android.mk
@@ -12,7 +12,6 @@
liblog libcutils
include $(CLEAR_VARS)
-
LOCAL_SRC_FILES := $(libsuspend_src_files)
LOCAL_MODULE := libsuspend
LOCAL_MODULE_TAGS := optional
@@ -21,3 +20,12 @@
LOCAL_SHARED_LIBRARIES := $(libsuspend_libraries)
#LOCAL_CFLAGS += -DLOG_NDEBUG=0
include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(libsuspend_src_files)
+LOCAL_MODULE := libsuspend
+LOCAL_MODULE_TAGS := optional
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
+#LOCAL_CFLAGS += -DLOG_NDEBUG=0
+include $(BUILD_STATIC_LIBRARY)
diff --git a/libsysutils/src/SocketClient.cpp b/libsysutils/src/SocketClient.cpp
index 3d4984d..ae0e077 100644
--- a/libsysutils/src/SocketClient.cpp
+++ b/libsysutils/src/SocketClient.cpp
@@ -112,6 +112,12 @@
char *result = (char *)malloc(len * 2 + 3);
char *current = result;
const char *end = arg + len;
+ char *oldresult;
+
+ if(result == NULL) {
+ SLOGW("malloc error (%s)", strerror(errno));
+ return NULL;
+ }
*(current++) = '"';
while (arg < end) {
@@ -125,8 +131,9 @@
}
*(current++) = '"';
*(current++) = '\0';
+ oldresult = result; // save pointer in case realloc fails
result = (char *)realloc(result, current-result);
- return result;
+ return result ? result : oldresult;
}
diff --git a/mkbootimg/mkbootimg.c b/mkbootimg/mkbootimg.c
index 2c32ce3..34a879b 100644
--- a/mkbootimg/mkbootimg.c
+++ b/mkbootimg/mkbootimg.c
@@ -72,7 +72,7 @@
-static unsigned char padding[4096] = { 0, };
+static unsigned char padding[16384] = { 0, };
int write_padding(int fd, unsigned pagesize, unsigned itemsize)
{
@@ -152,7 +152,8 @@
board = val;
} else if(!strcmp(arg,"--pagesize")) {
pagesize = strtoul(val, 0, 10);
- if ((pagesize != 2048) && (pagesize != 4096)) {
+ if ((pagesize != 2048) && (pagesize != 4096)
+ && (pagesize != 8192) && (pagesize != 16384)) {
fprintf(stderr,"error: unsupported page size %d\n", pagesize);
return -1;
}
diff --git a/rootdir/etc/init.goldfish.rc b/rootdir/etc/init.goldfish.rc
index a0c1c4f..d227bbe 100644
--- a/rootdir/etc/init.goldfish.rc
+++ b/rootdir/etc/init.goldfish.rc
@@ -3,6 +3,7 @@
mkdir /mnt/sdcard 0000 system system
# for backwards compatibility
symlink /mnt/sdcard /sdcard
+ mount debugfs debugfs /sys/kernel/debug
on boot
setsebool in_qemu 1
@@ -12,6 +13,7 @@
setprop ARGH ARGH
setprop net.eth0.gw 10.0.2.2
setprop net.eth0.dns1 10.0.2.3
+ setprop net.dns1 10.0.2.3
setprop net.gprs.local-ip 10.0.2.15
setprop ro.radio.use-ppp no
setprop ro.build.product generic
diff --git a/rootdir/init.rc b/rootdir/init.rc
index a76602c..5206cd1 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -37,7 +37,7 @@
export ANDROID_STORAGE /storage
export ASEC_MOUNTPOINT /mnt/asec
export LOOP_MOUNTPOINT /mnt/obb
- export BOOTCLASSPATH /system/framework/core.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/telephony-common.jar:/system/framework/mms-common.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/apache-xml.jar
+ export BOOTCLASSPATH /system/framework/core.jar:/system/framework/conscrypt.jar:/system/framework/okhttp.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/telephony-common.jar:/system/framework/mms-common.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/apache-xml.jar
# Backward compatibility
symlink /system/etc /etc
@@ -92,6 +92,7 @@
write /proc/sys/kernel/kptr_restrict 2
write /proc/sys/kernel/dmesg_restrict 1
write /proc/sys/vm/mmap_min_addr 32768
+ write /proc/sys/net/ipv4/ping_group_range "0 2147483647"
write /proc/sys/kernel/sched_rt_runtime_us 950000
write /proc/sys/kernel/sched_rt_period_us 1000000
@@ -120,6 +121,12 @@
write /dev/cpuctl/apps/bg_non_interactive/cpu.rt_runtime_us 700000
write /dev/cpuctl/apps/bg_non_interactive/cpu.rt_period_us 1000000
+# qtaguid will limit access to specific data based on group memberships.
+# net_bw_acct grants impersonation of socket owners.
+# net_bw_stats grants access to other apps' detailed tagged-socket stats.
+ chown root net_bw_acct /proc/net/xt_qtaguid/ctrl
+ chown root net_bw_stats /proc/net/xt_qtaguid/stats
+
# Allow everybody to read the xt_qtaguid resource tracking misc dev.
# This is needed by any process that uses socket tagging.
chmod 0644 /dev/xt_qtaguid
@@ -235,6 +242,9 @@
# the following directory.
mkdir /data/drm 0770 drm drm
+ # Separate location for storing security policy files on data
+ mkdir /data/security 0711 system system
+
# If there is no fs-post-data action in the init.<device>.rc file, you
# must uncomment this line, otherwise encrypted filesystems
# won't work.
@@ -294,6 +304,8 @@
chown system system /sys/devices/system/cpu/cpufreq/interactive/boostpulse
chown system system /sys/devices/system/cpu/cpufreq/interactive/input_boost
chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/input_boost
+ chown system system /sys/devices/system/cpu/cpufreq/interactive/boostpulse_duration
+ chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/boostpulse_duration
# Assume SMP uses shared cpufreq policy for all CPUs
chown system system /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq
@@ -310,12 +322,6 @@
chown system system /sys/class/leds/red/device/grpfreq
chown system system /sys/class/leds/red/device/grppwm
chown system system /sys/class/leds/red/device/blink
- chown system system /sys/class/leds/red/brightness
- chown system system /sys/class/leds/green/brightness
- chown system system /sys/class/leds/blue/brightness
- chown system system /sys/class/leds/red/device/grpfreq
- chown system system /sys/class/leds/red/device/grppwm
- chown system system /sys/class/leds/red/device/blink
chown system system /sys/class/timed_output/vibrator/enable
chown system system /sys/module/sco/parameters/disable_esco
chown system system /sys/kernel/ipv4/tcp_wmem_min
diff --git a/rootdir/init.usb.rc b/rootdir/init.usb.rc
index 15467cc..f37b630 100644
--- a/rootdir/init.usb.rc
+++ b/rootdir/init.usb.rc
@@ -88,4 +88,5 @@
# Used to set USB configuration at boot and to switch the configuration
# when changing the default configuration
on property:persist.sys.usb.config=*
+ setprop sys.usb.config none
setprop sys.usb.config ${persist.sys.usb.config}
diff --git a/run-as/Android.mk b/run-as/Android.mk
index 043cc3a..a8f2885 100644
--- a/run-as/Android.mk
+++ b/run-as/Android.mk
@@ -3,6 +3,8 @@
LOCAL_SRC_FILES:= run-as.c package.c
+LOCAL_SHARED_LIBRARIES := libselinux
+
LOCAL_MODULE:= run-as
include $(BUILD_EXECUTABLE)
diff --git a/run-as/package.c b/run-as/package.c
index 143d647..27fc1eb 100644
--- a/run-as/package.c
+++ b/run-as/package.c
@@ -47,15 +47,18 @@
/* Copy 'srclen' string bytes from 'src' into buffer 'dst' of size 'dstlen'
* This function always zero-terminate the destination buffer unless
* 'dstlen' is 0, even in case of overflow.
+ * Returns a pointer into the src string, leaving off where the copy
+ * has stopped. The copy will stop when dstlen, srclen or a null
+ * character on src has been reached.
*/
-static void
+static const char*
string_copy(char* dst, size_t dstlen, const char* src, size_t srclen)
{
const char* srcend = src + srclen;
const char* dstend = dst + dstlen;
if (dstlen == 0)
- return;
+ return src;
dstend--; /* make room for terminating zero */
@@ -63,6 +66,7 @@
*dst++ = *src++;
*dst = '\0'; /* zero-terminate result */
+ return src;
}
/* Open 'filename' and map it into our address-space.
@@ -76,13 +80,30 @@
struct stat st;
size_t length = 0;
void* address = NULL;
+ gid_t oldegid;
*filesize = 0;
+ /*
+ * Temporarily switch effective GID to allow us to read
+ * the packages file
+ */
+
+ oldegid = getegid();
+ if (setegid(AID_SYSTEM) < 0) {
+ return NULL;
+ }
+
/* open the file for reading */
fd = TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
- if (fd < 0)
+ if (fd < 0) {
return NULL;
+ }
+
+ /* restore back to our old egid */
+ if (setegid(oldegid) < 0) {
+ goto EXIT;
+ }
/* get its size */
ret = TEMP_FAILURE_RETRY(fstat(fd, &st));
@@ -411,6 +432,7 @@
info->uid = 0;
info->isDebuggable = 0;
info->dataDir[0] = '\0';
+ info->seinfo[0] = '\0';
buffer = map_file(PACKAGES_LIST_FILE, &buffer_len);
if (buffer == NULL)
@@ -421,13 +443,14 @@
/* expect the following format on each line of the control file:
*
- * <pkgName> <uid> <debugFlag> <dataDir>
+ * <pkgName> <uid> <debugFlag> <dataDir> <seinfo>
*
* where:
* <pkgName> is the package's name
* <uid> is the application-specific user Id (decimal)
* <debugFlag> is 1 if the package is debuggable, or 0 otherwise
* <dataDir> is the path to the package's data directory (e.g. /data/data/com.example.foo)
+ * <seinfo> is the seinfo label associated with the package
*
* The file is generated in com.android.server.PackageManagerService.Settings.writeLP()
*/
@@ -483,7 +506,18 @@
if (q == p)
goto BAD_FORMAT;
- string_copy(info->dataDir, sizeof info->dataDir, p, q - p);
+ p = string_copy(info->dataDir, sizeof info->dataDir, p, q - p);
+
+ /* skip spaces */
+ if (parse_spaces(&p, end) < 0)
+ goto BAD_FORMAT;
+
+ /* fifth field is the seinfo string */
+ q = skip_non_spaces(p, end);
+ if (q == p)
+ goto BAD_FORMAT;
+
+ string_copy(info->seinfo, sizeof info->seinfo, p, q - p);
/* Ignore the rest */
result = 0;
diff --git a/run-as/package.h b/run-as/package.h
index 852af06..34603c0 100644
--- a/run-as/package.h
+++ b/run-as/package.h
@@ -30,6 +30,7 @@
uid_t uid;
char isDebuggable;
char dataDir[PATH_MAX];
+ char seinfo[PATH_MAX];
} PackageInfo;
/* see documentation in package.c for these functiosn */
diff --git a/run-as/run-as.c b/run-as/run-as.c
index 20e1530..3c0ecc4 100644
--- a/run-as/run-as.c
+++ b/run-as/run-as.c
@@ -29,6 +29,7 @@
#include <time.h>
#include <stdarg.h>
+#include <selinux/android.h>
#include <private/android_filesystem_config.h>
#include "package.h"
@@ -162,6 +163,11 @@
return 1;
}
+ if (selinux_android_setcontext(uid, 0, info.seinfo, pkgname) < 0) {
+ panic("Could not set SELinux security context: %s\n", strerror(errno));
+ return 1;
+ }
+
/* User specified command for exec. */
if (argc >= 3 ) {
if (execvp(argv[2], argv+2) < 0) {
diff --git a/toolbox/dd.c b/toolbox/dd.c
index 350f1d2..a8c12d2 100644
--- a/toolbox/dd.c
+++ b/toolbox/dd.c
@@ -590,8 +590,8 @@
/* If not a pipe or tape device, try to seek on it. */
if (!(in.flags & (ISPIPE|ISTAPE))) {
- if (lseek(in.fd,
- (off_t)in.offset * (off_t)in.dbsz, SEEK_CUR) == -1) {
+ if (lseek64(in.fd,
+ (off64_t)in.offset * (off64_t)in.dbsz, SEEK_CUR) == -1) {
fprintf(stderr, "%s: seek error: %s",
in.name, strerror(errno));
exit(1);
@@ -661,8 +661,8 @@
* have specified the seek operand.
*/
if (!(out.flags & ISTAPE)) {
- if (lseek(out.fd,
- (off_t)out.offset * (off_t)out.dbsz, SEEK_SET) == -1) {
+ if (lseek64(out.fd,
+ (off64_t)out.offset * (off64_t)out.dbsz, SEEK_SET) == -1) {
fprintf(stderr, "%s: seek error: %s\n",
out.name, strerror(errno));
exit(1);
diff --git a/toolbox/dmesg.c b/toolbox/dmesg.c
index e57f607..9c73b00 100644
--- a/toolbox/dmesg.c
+++ b/toolbox/dmesg.c
@@ -5,15 +5,30 @@
#include <sys/klog.h>
#include <string.h>
-#define KLOG_BUF_SHIFT 17 /* CONFIG_LOG_BUF_SHIFT from our kernel */
-#define KLOG_BUF_LEN (1 << KLOG_BUF_SHIFT)
+#define FALLBACK_KLOG_BUF_SHIFT 17 /* CONFIG_LOG_BUF_SHIFT from our kernel */
+#define FALLBACK_KLOG_BUF_LEN (1 << FALLBACK_KLOG_BUF_SHIFT)
int dmesg_main(int argc, char **argv)
{
- char buffer[KLOG_BUF_LEN + 1];
- char *p = buffer;
+ char *buffer;
+ char *p;
ssize_t ret;
- int n, op;
+ int n, op, klog_buf_len;
+
+ klog_buf_len = klogctl(KLOG_SIZE_BUFFER, 0, 0);
+
+ if (klog_buf_len <= 0) {
+ klog_buf_len = FALLBACK_KLOG_BUF_LEN;
+ }
+
+ buffer = (char *)malloc(klog_buf_len + 1);
+
+ if (!buffer) {
+ perror("malloc");
+ return EXIT_FAILURE;
+ }
+
+ p = buffer;
if((argc == 2) && (!strcmp(argv[1],"-c"))) {
op = KLOG_READ_CLEAR;
@@ -21,7 +36,7 @@
op = KLOG_READ_ALL;
}
- n = klogctl(op, buffer, KLOG_BUF_LEN);
+ n = klogctl(op, buffer, klog_buf_len);
if (n < 0) {
perror("klogctl");
return EXIT_FAILURE;
diff --git a/toolbox/ls.c b/toolbox/ls.c
index e530521..5324511 100644
--- a/toolbox/ls.c
+++ b/toolbox/ls.c
@@ -28,6 +28,7 @@
#define LIST_LONG_NUMERIC (1 << 5)
#define LIST_CLASSIFY (1 << 6)
#define LIST_MACLABEL (1 << 7)
+#define LIST_INODE (1 << 8)
// fwd
static int listpath(const char *name, int flags);
@@ -127,22 +128,20 @@
return 0;
}
-static int listfile_size(const char *path, const char *filename, int flags)
+static int listfile_size(const char *path, const char *filename, struct stat *s,
+ int flags)
{
- struct stat s;
-
- if (lstat(path, &s) < 0) {
- fprintf(stderr, "lstat '%s' failed: %s\n", path, strerror(errno));
+ if(!s || !path) {
return -1;
}
/* blocks are 512 bytes, we want output to be KB */
if ((flags & LIST_SIZE) != 0) {
- printf("%lld ", s.st_blocks / 2);
+ printf("%lld ", s->st_blocks / 2);
}
if ((flags & LIST_CLASSIFY) != 0) {
- char filetype = mode2kind(s.st_mode);
+ char filetype = mode2kind(s->st_mode);
if (filetype != 'l') {
printf("%c ", filetype);
} else {
@@ -161,15 +160,18 @@
return 0;
}
-static int listfile_long(const char *path, int flags)
+static int listfile_long(const char *path, struct stat *s, int flags)
{
- struct stat s;
char date[32];
char mode[16];
char user[16];
char group[16];
const char *name;
+ if(!s || !path) {
+ return -1;
+ }
+
/* name is anything after the final '/', or the whole path if none*/
name = strrchr(path, '/');
if(name == 0) {
@@ -178,36 +180,32 @@
name++;
}
- if(lstat(path, &s) < 0) {
- return -1;
- }
-
- mode2str(s.st_mode, mode);
+ mode2str(s->st_mode, mode);
if (flags & LIST_LONG_NUMERIC) {
- sprintf(user, "%ld", s.st_uid);
- sprintf(group, "%ld", s.st_gid);
+ sprintf(user, "%ld", s->st_uid);
+ sprintf(group, "%ld", s->st_gid);
} else {
- user2str(s.st_uid, user);
- group2str(s.st_gid, group);
+ user2str(s->st_uid, user);
+ group2str(s->st_gid, group);
}
- strftime(date, 32, "%Y-%m-%d %H:%M", localtime((const time_t*)&s.st_mtime));
+ strftime(date, 32, "%Y-%m-%d %H:%M", localtime((const time_t*)&s->st_mtime));
date[31] = 0;
// 12345678901234567890123456789012345678901234567890123456789012345678901234567890
// MMMMMMMM UUUUUUUU GGGGGGGGG XXXXXXXX YYYY-MM-DD HH:MM NAME (->LINK)
- switch(s.st_mode & S_IFMT) {
+ switch(s->st_mode & S_IFMT) {
case S_IFBLK:
case S_IFCHR:
printf("%s %-8s %-8s %3d, %3d %s %s\n",
mode, user, group,
- (int) MAJOR(s.st_rdev), (int) MINOR(s.st_rdev),
+ (int) MAJOR(s->st_rdev), (int) MINOR(s->st_rdev),
date, name);
break;
case S_IFREG:
printf("%s %-8s %-8s %8lld %s %s\n",
- mode, user, group, s.st_size, date, name);
+ mode, user, group, s->st_size, date, name);
break;
case S_IFLNK: {
char linkto[256];
@@ -237,15 +235,18 @@
return 0;
}
-static int listfile_maclabel(const char *path, int flags)
+static int listfile_maclabel(const char *path, struct stat *s, int flags)
{
- struct stat s;
char mode[16];
char user[16];
char group[16];
char *maclabel = NULL;
const char *name;
+ if(!s || !path) {
+ return -1;
+ }
+
/* name is anything after the final '/', or the whole path if none*/
name = strrchr(path, '/');
if(name == 0) {
@@ -254,20 +255,16 @@
name++;
}
- if(lstat(path, &s) < 0) {
- return -1;
- }
-
lgetfilecon(path, &maclabel);
if (!maclabel) {
return -1;
}
- mode2str(s.st_mode, mode);
- user2str(s.st_uid, user);
- group2str(s.st_gid, group);
+ mode2str(s->st_mode, mode);
+ user2str(s->st_uid, user);
+ group2str(s->st_gid, group);
- switch(s.st_mode & S_IFMT) {
+ switch(s->st_mode & S_IFMT) {
case S_IFLNK: {
char linkto[256];
ssize_t len;
@@ -301,7 +298,9 @@
static int listfile(const char *dirname, const char *filename, int flags)
{
- if ((flags & (LIST_LONG | LIST_SIZE | LIST_CLASSIFY | LIST_MACLABEL)) == 0) {
+ struct stat s;
+
+ if ((flags & (LIST_LONG | LIST_SIZE | LIST_CLASSIFY | LIST_MACLABEL | LIST_INODE)) == 0) {
printf("%s\n", filename);
return 0;
}
@@ -316,12 +315,20 @@
pathname = filename;
}
+ if(lstat(pathname, &s) < 0) {
+ return -1;
+ }
+
+ if(flags & LIST_INODE) {
+ printf("%8llu ", s.st_ino);
+ }
+
if ((flags & LIST_MACLABEL) != 0) {
- return listfile_maclabel(pathname, flags);
+ return listfile_maclabel(pathname, &s, flags);
} else if ((flags & LIST_LONG) != 0) {
- return listfile_long(pathname, flags);
+ return listfile_long(pathname, &s, flags);
} else /*((flags & LIST_SIZE) != 0)*/ {
- return listfile_size(pathname, filename, flags);
+ return listfile_size(pathname, filename, &s, flags);
}
}
@@ -456,6 +463,7 @@
case 'Z': flags |= LIST_MACLABEL; break;
case 'a': flags |= LIST_ALL; break;
case 'F': flags |= LIST_CLASSIFY; break;
+ case 'i': flags |= LIST_INODE; break;
default:
fprintf(stderr, "%s: Unknown option '-%c'. Aborting.\n", "ls", arg[0]);
exit(1);
diff --git a/toolbox/mount.c b/toolbox/mount.c
index b7adce2..bcda2a2 100644
--- a/toolbox/mount.c
+++ b/toolbox/mount.c
@@ -19,7 +19,7 @@
#define LOOPDEV_MAXLEN 64
struct mount_opts {
- const char str[8];
+ const char str[16];
unsigned long rwmask;
unsigned long rwset;
unsigned long rwnoset;
@@ -65,10 +65,11 @@
static void add_extra_option(struct extra_opts *extra, char *s)
{
int len = strlen(s);
- int newlen = extra->used_size + len;
+ int newlen;
if (extra->str)
len++; /* +1 for ',' */
+ newlen = extra->used_size + len;
if (newlen >= extra->alloc_size) {
char *new;
@@ -79,7 +80,7 @@
extra->str = new;
extra->end = extra->str + extra->used_size;
- extra->alloc_size = newlen;
+ extra->alloc_size = newlen + 1;
}
if (extra->used_size) {
diff --git a/toolbox/netstat.c b/toolbox/netstat.c
index 5768599..05dc640 100644
--- a/toolbox/netstat.c
+++ b/toolbox/netstat.c
@@ -108,7 +108,7 @@
addr2str(AF_INET, &raddr, rport, rip);
printf("%4s %6d %6d %-22s %-22s %s\n",
- label, txq, rxq, lip, rip,
+ label, rxq, txq, lip, rip,
state2str(state));
}
}
@@ -136,7 +136,7 @@
addr2str(AF_INET6, &raddr6, rport, rip);
printf("%4s %6d %6d %-22s %-22s %s\n",
- label, txq, rxq, lip, rip,
+ label, rxq, txq, lip, rip,
state2str(state));
}
}
diff --git a/toolbox/watchprops.c b/toolbox/watchprops.c
index d311992..bf82882 100644
--- a/toolbox/watchprops.c
+++ b/toolbox/watchprops.c
@@ -1,35 +1,30 @@
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
+#include <errno.h>
#include <cutils/properties.h>
+#include <cutils/hashmap.h>
#include <sys/atomics.h>
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
#include <sys/_system_properties.h>
-
-extern prop_area *__system_property_area__;
-
-typedef struct pwatch pwatch;
-
-struct pwatch
+static int str_hash(void *key)
{
- const prop_info *pi;
- unsigned serial;
-};
+ return hashmapHash(key, strlen(key));
+}
-static pwatch watchlist[1024];
-
-static void announce(const prop_info *pi)
+static bool str_equals(void *keyA, void *keyB)
{
- char name[PROP_NAME_MAX];
- char value[PROP_VALUE_MAX];
+ return strcmp(keyA, keyB) == 0;
+}
+
+static void announce(char *name, char *value)
+{
char *x;
- __system_property_read(pi, name, value);
-
for(x = value; *x; x++) {
if((*x < 32) || (*x > 127)) *x = '.';
}
@@ -37,40 +32,64 @@
fprintf(stderr,"%10d %s = '%s'\n", (int) time(0), name, value);
}
+static void add_to_watchlist(Hashmap *watchlist, const char *name,
+ const prop_info *pi)
+{
+ char *key = strdup(name);
+ unsigned *value = malloc(sizeof(unsigned));
+ if (!key || !value)
+ exit(1);
+
+ *value = __system_property_serial(pi);
+ hashmapPut(watchlist, key, value);
+}
+
+static void populate_watchlist(const prop_info *pi, void *cookie)
+{
+ Hashmap *watchlist = cookie;
+ char name[PROP_NAME_MAX];
+ char value_unused[PROP_VALUE_MAX];
+
+ __system_property_read(pi, name, value_unused);
+ add_to_watchlist(watchlist, name, pi);
+}
+
+static void update_watchlist(const prop_info *pi, void *cookie)
+{
+ Hashmap *watchlist = cookie;
+ char name[PROP_NAME_MAX];
+ char value[PROP_VALUE_MAX];
+ unsigned *serial;
+
+ __system_property_read(pi, name, value);
+ serial = hashmapGet(watchlist, name);
+ if (!serial) {
+ add_to_watchlist(watchlist, name, pi);
+ announce(name, value);
+ } else {
+ unsigned tmp = __system_property_serial(pi);
+ if (*serial != tmp) {
+ *serial = tmp;
+ announce(name, value);
+ }
+ }
+}
+
int watchprops_main(int argc, char *argv[])
{
- prop_area *pa = __system_property_area__;
- unsigned serial = pa->serial;
- unsigned count = pa->count;
+ unsigned serial = 0;
+ unsigned count = 0;
unsigned n;
- if(count >= 1024) exit(1);
+ Hashmap *watchlist = hashmapCreate(1024, str_hash, str_equals);
+ if (!watchlist)
+ exit(1);
- for(n = 0; n < count; n++) {
- watchlist[n].pi = __system_property_find_nth(n);
- watchlist[n].serial = watchlist[n].pi->serial;
- }
+ __system_property_foreach(populate_watchlist, watchlist);
for(;;) {
- do {
- __futex_wait(&pa->serial, serial, 0);
- } while(pa->serial == serial);
-
- while(count < pa->count){
- watchlist[count].pi = __system_property_find_nth(count);
- watchlist[count].serial = watchlist[n].pi->serial;
- announce(watchlist[count].pi);
- count++;
- if(count == 1024) exit(1);
- }
-
- for(n = 0; n < count; n++){
- unsigned tmp = watchlist[n].pi->serial;
- if(watchlist[n].serial != tmp) {
- announce(watchlist[n].pi);
- watchlist[n].serial = tmp;
- }
- }
+ serial = __system_property_wait_any(serial);
+ __system_property_foreach(update_watchlist, watchlist);
}
return 0;
}