diff options
183 files changed, 15162 insertions, 8383 deletions
diff --git a/adb/Android.mk b/adb/Android.mk index 1a25106..32dd95a 100644 --- a/adb/Android.mk +++ b/adb/Android.mk @@ -16,7 +16,7 @@ EXTRA_SRCS := 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) @@ -33,16 +33,16 @@ endif ifeq ($(HOST_OS),windows) USB_SRCS := usb_windows.c - EXTRA_SRCS := get_my_path_windows.c + EXTRA_SRCS := get_my_path_windows.c ../libcutils/list.c EXTRA_STATIC_LIBS := AdbWinApi ifneq ($(strip $(USE_CYGWIN)),) # Pure cygwin case - LOCAL_LDLIBS += -lpthread + LOCAL_LDLIBS += -lpthread -lgdi32 LOCAL_C_INCLUDES += /usr/include/w32api/ddk endif ifneq ($(strip $(USE_MINGW)),) # MinGW under Linux case - LOCAL_LDLIBS += -lws2_32 + LOCAL_LDLIBS += -lws2_32 -lgdi32 USE_SYSDEPS_WIN32 := 1 LOCAL_C_INCLUDES += /usr/i586-mingw32msvc/include/ddk endif @@ -57,6 +57,7 @@ LOCAL_SRC_FILES := \ transport_usb.c \ commandline.c \ adb_client.c \ + adb_auth_host.c \ sockets.c \ services.c \ file_sync_client.c \ @@ -65,6 +66,7 @@ LOCAL_SRC_FILES := \ utils.c \ usb_vendors.c +LOCAL_C_INCLUDES += external/openssl/include ifneq ($(USE_SYSDEPS_WIN32),) LOCAL_SRC_FILES += sysdeps_win32.c @@ -76,14 +78,14 @@ LOCAL_CFLAGS += -O2 -g -DADB_HOST=1 -Wall -Wno-unused-parameter LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE LOCAL_MODULE := adb -LOCAL_STATIC_LIBRARIES := libzipfile libunz $(EXTRA_STATIC_LIBS) +LOCAL_STATIC_LIBRARIES := libzipfile libunz libcrypto_static $(EXTRA_STATIC_LIBS) ifeq ($(USE_SYSDEPS_WIN32),) LOCAL_STATIC_LIBRARIES += libcutils endif include $(BUILD_HOST_EXECUTABLE) -$(call dist-for-goals,dist_files,$(LOCAL_BUILT_MODULE)) +$(call dist-for-goals,dist_files sdk,$(LOCAL_BUILT_MODULE)) ifeq ($(HOST_OS),windows) $(LOCAL_INSTALLED_MODULE): \ @@ -104,6 +106,7 @@ LOCAL_SRC_FILES := \ transport.c \ transport_local.c \ transport_usb.c \ + adb_auth_client.c \ sockets.c \ services.c \ file_sync_service.c \ @@ -127,7 +130,7 @@ LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN) LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED) -LOCAL_STATIC_LIBRARIES := libcutils libc +LOCAL_STATIC_LIBRARIES := libcutils libc libmincrypt include $(BUILD_EXECUTABLE) @@ -136,7 +139,7 @@ include $(BUILD_EXECUTABLE) ifneq ($(SDK_ONLY),true) include $(CLEAR_VARS) -LOCAL_LDLIBS := -lrt -lncurses -lpthread +LOCAL_LDLIBS := -lrt -ldl -lpthread LOCAL_SRC_FILES := \ adb.c \ @@ -146,6 +149,7 @@ LOCAL_SRC_FILES := \ transport_usb.c \ commandline.c \ adb_client.c \ + adb_auth_host.c \ sockets.c \ services.c \ file_sync_client.c \ @@ -165,9 +169,13 @@ LOCAL_CFLAGS := \ -D_XOPEN_SOURCE \ -D_GNU_SOURCE +LOCAL_C_INCLUDES += external/openssl/include + LOCAL_MODULE := adb LOCAL_STATIC_LIBRARIES := libzipfile libunz libcutils +LOCAL_SHARED_LIBRARIES := libcrypto + include $(BUILD_EXECUTABLE) endif diff --git a/adb/SERVICES.TXT b/adb/SERVICES.TXT index be4d50b..b53bc44 100644 --- a/adb/SERVICES.TXT +++ b/adb/SERVICES.TXT @@ -17,8 +17,10 @@ host:kill upgrade. host:devices +host:devices-l Ask to return the list of available Android devices and their - state. After the OKAY, this is followed by a 4-byte hex len, + state. devices-l includes the device paths in the state. + After the OKAY, this is followed by a 4-byte hex len, and a string that will be dumped as-is by the client, then the connection is closed @@ -88,6 +90,9 @@ host:<request> Returns the serial number of the corresponding device/emulator. Note that emulator serial numbers are of the form "emulator-5554" +<host-prefix>:get-devpath + Returns the device path of the corresponding device/emulator. + <host-prefix>:get-state Returns the state of a given device as a string. @@ -112,7 +117,34 @@ host:<request> or even any one of the local services described below. +<host-prefix>:forward:norebind:<local>;<remote> + Same as <host-prefix>:forward:<local>;<remote> except that it will + fail it there is already a forward connection from <local>. + + Used to implement 'adb forward --no-rebind <local> <remote>' + +<host-prefix>:killforward:<local> + Remove any existing forward local connection from <local>. + This is used to implement 'adb forward --remove <local>' + +<host-prefix>:killforward-all + Remove all forward network connections. + This is used to implement 'adb forward --remove-all'. + +<host-prefix>:list-forward + List all existing forward connections from this server. + This returns something that looks like the following: + + <hex4>: The length of the payload, as 4 hexadecimal chars. + <payload>: A series of lines of the following format: + + <serial> " " <local> " " <remote> "\n" + + Where <serial> is a device serial number. + <local> is the host-specific endpoint (e.g. tcp:9000). + <remote> is the device-specific endpoint. + Used to implement 'adb forward --list'. LOCAL SERVICES: @@ -21,17 +21,23 @@ #include <ctype.h> #include <stdarg.h> #include <errno.h> +#include <stddef.h> #include <string.h> #include <time.h> #include <sys/time.h> +#include <stdint.h> #include "sysdeps.h" #include "adb.h" +#include "adb_auth.h" + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) #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 #include "usb_vendors.h" #endif @@ -41,8 +47,13 @@ ADB_MUTEX_DEFINE( D_lock ); #endif int HOST = 0; +int gListenAll = 0; + +static int auth_enabled = 0; +#if !ADB_HOST static const char *adb_device_banner = "device"; +#endif void fatal(const char *fmt, ...) { @@ -94,6 +105,7 @@ void adb_trace_init(void) { "transport", TRACE_TRANSPORT }, { "jdwp", TRACE_JDWP }, { "services", TRACE_SERVICES }, + { "auth", TRACE_AUTH }, { NULL, 0 } }; @@ -197,19 +209,21 @@ void put_apacket(apacket *p) free(p); } -void handle_online(void) +void handle_online(atransport *t) { D("adb: online\n"); + t->online = 1; } void handle_offline(atransport *t) { D("adb: offline\n"); //Close the associated usb + t->online = 0; run_transport_disconnects(t); } -#if TRACE_PACKETS +#if DEBUG_PACKETS #define DUMPMAX 32 void print_packet(const char *label, apacket *p) { @@ -224,6 +238,7 @@ void print_packet(const char *label, apacket *p) case A_OKAY: tag = "OKAY"; break; case A_CLSE: tag = "CLSE"; break; case A_WRTE: tag = "WRTE"; break; + case A_AUTH: tag = "AUTH"; break; default: tag = "????"; break; } @@ -245,7 +260,7 @@ void print_packet(const char *label, apacket *p) } x++; } - fprintf(stderr, tag); + fputs(tag, stderr); } #endif @@ -269,6 +284,36 @@ static void send_close(unsigned local, unsigned remote, atransport *t) send_packet(p, t); } +static size_t fill_connect_data(char *buf, size_t bufsize) +{ +#if ADB_HOST + return snprintf(buf, bufsize, "host::") + 1; +#else + static const char *cnxn_props[] = { + "ro.product.name", + "ro.product.model", + "ro.product.device", + }; + static const int num_cnxn_props = ARRAY_SIZE(cnxn_props); + int i; + size_t remaining = bufsize; + size_t len; + + len = snprintf(buf, remaining, "%s::", adb_device_banner); + remaining -= len; + buf += len; + for (i = 0; i < num_cnxn_props; i++) { + char value[PROPERTY_VALUE_MAX]; + property_get(cnxn_props[i], value, ""); + len = snprintf(buf, remaining, "%s=%s;", cnxn_props[i], value); + remaining -= len; + buf += len; + } + + return bufsize - remaining + 1; +#endif +} + static void send_connect(atransport *t) { D("Calling send_connect \n"); @@ -276,15 +321,73 @@ static void send_connect(atransport *t) cp->msg.command = A_CNXN; cp->msg.arg0 = A_VERSION; cp->msg.arg1 = MAX_PAYLOAD; - snprintf((char*) cp->data, sizeof cp->data, "%s::", - HOST ? "host" : adb_device_banner); - cp->msg.data_length = strlen((char*) cp->data) + 1; + cp->msg.data_length = fill_connect_data((char *)cp->data, + sizeof(cp->data)); send_packet(cp, t); -#if ADB_HOST - /* XXX why sleep here? */ - // allow the device some time to respond to the connect message - adb_sleep_ms(1000); -#endif +} + +static void send_auth_request(atransport *t) +{ + D("Calling send_auth_request\n"); + apacket *p; + int ret; + + ret = adb_auth_generate_token(t->token, sizeof(t->token)); + if (ret != sizeof(t->token)) { + D("Error generating token ret=%d\n", ret); + return; + } + + p = get_apacket(); + memcpy(p->data, t->token, ret); + p->msg.command = A_AUTH; + p->msg.arg0 = ADB_AUTH_TOKEN; + p->msg.data_length = ret; + send_packet(p, t); +} + +static void send_auth_response(uint8_t *token, size_t token_size, atransport *t) +{ + D("Calling send_auth_response\n"); + apacket *p = get_apacket(); + int ret; + + ret = adb_auth_sign(t->key, token, token_size, p->data); + if (!ret) { + D("Error signing the token\n"); + put_apacket(p); + return; + } + + p->msg.command = A_AUTH; + p->msg.arg0 = ADB_AUTH_SIGNATURE; + p->msg.data_length = ret; + send_packet(p, t); +} + +static void send_auth_publickey(atransport *t) +{ + D("Calling send_auth_publickey\n"); + apacket *p = get_apacket(); + int ret; + + ret = adb_auth_get_userkey(p->data, sizeof(p->data)); + if (!ret) { + D("Failed to get user public key\n"); + put_apacket(p); + return; + } + + p->msg.command = A_AUTH; + p->msg.arg0 = ADB_AUTH_RSAPUBLICKEY; + p->msg.data_length = ret; + send_packet(p, t); +} + +void adb_auth_verified(atransport *t) +{ + handle_online(t); + send_connect(t); } static char *connection_state_name(atransport *t) @@ -305,29 +408,56 @@ static char *connection_state_name(atransport *t) } } +/* qual_overwrite is used to overwrite a qualifier string. dst is a + * pointer to a char pointer. It is assumed that if *dst is non-NULL, it + * was malloc'ed and needs to freed. *dst will be set to a dup of src. + */ +static void qual_overwrite(char **dst, const char *src) +{ + if (!dst) + return; + + free(*dst); + *dst = NULL; + + if (!src || !*src) + return; + + *dst = strdup(src); +} + void parse_banner(char *banner, atransport *t) { - char *type, *product, *end; + static const char *prop_seps = ";"; + static const char key_val_sep = '='; + char *cp; + char *type; D("parse_banner: %s\n", banner); type = banner; - product = strchr(type, ':'); - if(product) { - *product++ = 0; - } else { - product = ""; - } - - /* remove trailing ':' */ - end = strchr(product, ':'); - if(end) *end = 0; - - /* save product name in device structure */ - if (t->product == NULL) { - t->product = strdup(product); - } else if (strcmp(product, t->product) != 0) { - free(t->product); - t->product = strdup(product); + cp = strchr(type, ':'); + if (cp) { + *cp++ = 0; + /* Nothing is done with second field. */ + cp = strchr(cp, ':'); + if (cp) { + char *save; + char *key; + key = adb_strtok_r(cp + 1, prop_seps, &save); + while (key) { + cp = strchr(key, key_val_sep); + if (cp) { + *cp++ = '\0'; + if (!strcmp(key, "ro.product.name")) + qual_overwrite(&t->product, cp); + else if (!strcmp(key, "ro.product.model")) + qual_overwrite(&t->model, cp); + else if (!strcmp(key, "ro.product.device")) + qual_overwrite(&t->device, cp); + } + key = adb_strtok_r(NULL, prop_seps, &save); + } + } } if(!strcmp(type, "bootloader")){ @@ -389,13 +519,42 @@ void handle_packet(apacket *p, atransport *t) t->connection_state = CS_OFFLINE; handle_offline(t); } + parse_banner((char*) p->data, t); - handle_online(); - if(!HOST) send_connect(t); + + if (HOST || !auth_enabled) { + handle_online(t); + if(!HOST) send_connect(t); + } else { + send_auth_request(t); + } + break; + + case A_AUTH: + if (p->msg.arg0 == ADB_AUTH_TOKEN) { + t->key = adb_auth_nextkey(t->key); + if (t->key) { + send_auth_response(p->data, p->msg.data_length, t); + } else { + /* No more private keys to try, send the public key */ + send_auth_publickey(t); + } + } else if (p->msg.arg0 == ADB_AUTH_SIGNATURE) { + if (adb_auth_verify(t->token, p->data, p->msg.data_length)) { + adb_auth_verified(t); + t->failed_auth_attempts = 0; + } else { + if (t->failed_auth_attempts++ > 10) + adb_sleep_ms(1000); + send_auth_request(t); + } + } else if (p->msg.arg0 == ADB_AUTH_RSAPUBLICKEY) { + adb_auth_confirm_key(p->data, p->msg.data_length, t); + } break; case A_OPEN: /* OPEN(local-id, 0, "destination") */ - if(t->connection_state != CS_OFFLINE) { + if (t->online) { char *name = (char*) p->data; name[p->msg.data_length > 0 ? p->msg.data_length - 1 : 0] = 0; s = create_local_service_socket(name); @@ -411,7 +570,7 @@ void handle_packet(apacket *p, atransport *t) break; case A_OKAY: /* READY(local-id, remote-id, "") */ - if(t->connection_state != CS_OFFLINE) { + if (t->online) { if((s = find_local_socket(p->msg.arg1))) { if(s->peer == 0) { s->peer = create_remote_socket(p->msg.arg0, t); @@ -423,7 +582,7 @@ void handle_packet(apacket *p, atransport *t) break; case A_CLSE: /* CLOSE(local-id, remote-id, "") */ - if(t->connection_state != CS_OFFLINE) { + if (t->online) { if((s = find_local_socket(p->msg.arg1))) { s->close(s); } @@ -431,7 +590,7 @@ void handle_packet(apacket *p, atransport *t) break; case A_WRTE: - if(t->connection_state != CS_OFFLINE) { + if (t->online) { if((s = find_local_socket(p->msg.arg1))) { unsigned rid = p->msg.arg0; p->len = p->msg.data_length; @@ -544,7 +703,13 @@ int local_name_to_fd(const char *name) if(!strncmp("tcp:", name, 4)){ int ret; port = atoi(name + 4); - ret = socket_loopback_server(port, SOCK_STREAM); + + if (gListenAll > 0) { + ret = socket_inaddr_any_server(port, SOCK_STREAM); + } else { + ret = socket_loopback_server(port, SOCK_STREAM); + } + return ret; } #ifndef HAVE_WIN32_IPC /* no Unix-domain sockets on Win32 */ @@ -565,24 +730,90 @@ int local_name_to_fd(const char *name) return -1; } -static int remove_listener(const char *local_name, const char *connect_to, atransport* transport) +// Write a single line describing a listener to a user-provided buffer. +// Appends a trailing zero, even in case of truncation, but the function +// returns the full line length. +// If |buffer| is NULL, does not write but returns required size. +static int format_listener(alistener* l, char* buffer, size_t buffer_len) { + // Format is simply: + // + // <device-serial> " " <local-name> " " <remote-name> "\n" + // + int local_len = strlen(l->local_name); + int connect_len = strlen(l->connect_to); + int serial_len = strlen(l->transport->serial); + + if (buffer != NULL) { + snprintf(buffer, buffer_len, "%s %s %s\n", + l->transport->serial, l->local_name, l->connect_to); + } + // NOTE: snprintf() on Windows returns -1 in case of truncation, so + // return the computed line length instead. + return local_len + connect_len + serial_len + 3; +} + +// Write the list of current listeners (network redirections) into a +// user-provided buffer. Appends a trailing zero, even in case of +// trunctaion, but return the full size in bytes. +// If |buffer| is NULL, does not write but returns required size. +static int format_listeners(char* buf, size_t buflen) +{ + alistener* l; + int result = 0; + for (l = listener_list.next; l != &listener_list; l = l->next) { + // Ignore special listeners like those for *smartsocket* + if (l->connect_to[0] == '*') + continue; + int len = format_listener(l, buf, buflen); + // Ensure there is space for the trailing zero. + result += len; + if (buf != NULL) { + buf += len; + buflen -= len; + if (buflen <= 0) + break; + } + } + return result; +} + +static int remove_listener(const char *local_name, atransport* transport) { alistener *l; for (l = listener_list.next; l != &listener_list; l = l->next) { - if (!strcmp(local_name, l->local_name) && - !strcmp(connect_to, l->connect_to) && - l->transport && l->transport == transport) { - - listener_disconnect(l, transport); + if (!strcmp(local_name, l->local_name)) { + listener_disconnect(l, l->transport); return 0; } } - return -1; } -static int install_listener(const char *local_name, const char *connect_to, atransport* transport) +static void remove_all_listeners(void) +{ + alistener *l, *l_next; + for (l = listener_list.next; l != &listener_list; l = l_next) { + l_next = l->next; + // Never remove smart sockets. + if (l->connect_to[0] == '*') + continue; + listener_disconnect(l, l->transport); + } +} + +// error/status codes for install_listener. +typedef enum { + INSTALL_STATUS_OK = 0, + INSTALL_STATUS_INTERNAL_ERROR = -1, + INSTALL_STATUS_CANNOT_BIND = -2, + INSTALL_STATUS_CANNOT_REBIND = -3, +} install_status_t; + +static install_status_t install_listener(const char *local_name, + const char *connect_to, + atransport* transport, + int no_rebind) { alistener *l; @@ -594,12 +825,17 @@ static int install_listener(const char *local_name, const char *connect_to, atra /* can't repurpose a smartsocket */ if(l->connect_to[0] == '*') { - return -1; + return INSTALL_STATUS_INTERNAL_ERROR; + } + + /* can't repurpose a listener if 'no_rebind' is true */ + if (no_rebind) { + return INSTALL_STATUS_CANNOT_REBIND; } cto = strdup(connect_to); if(cto == 0) { - return -1; + return INSTALL_STATUS_INTERNAL_ERROR; } //printf("rebinding '%s' to '%s'\n", local_name, connect_to); @@ -610,7 +846,7 @@ static int install_listener(const char *local_name, const char *connect_to, atra l->transport = transport; add_transport_disconnect(l->transport, &l->disconnect); } - return 0; + return INSTALL_STATUS_OK; } } @@ -647,11 +883,11 @@ static int install_listener(const char *local_name, const char *connect_to, atra l->disconnect.func = listener_disconnect; add_transport_disconnect(transport, &l->disconnect); } - return 0; + return INSTALL_STATUS_OK; nomem: fatal("cannot allocate listener"); - return 0; + return INSTALL_STATUS_INTERNAL_ERROR; } #ifdef HAVE_WIN32_PROC @@ -756,6 +992,7 @@ int launch_server(int server_port) /* 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; @@ -775,6 +1012,26 @@ int launch_server(int server_port) 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 ); @@ -851,8 +1108,10 @@ int launch_server(int server_port) dup2(fd[1], STDERR_FILENO); adb_close(fd[1]); + char str_port[30]; + snprintf(str_port, sizeof(str_port), "%d", server_port); // child process - int result = execl(path, "adb", "fork-server", "server", NULL); + int result = execl(path, "adb", "-P", str_port, "fork-server", "server", NULL); // this should not return fprintf(stderr, "OOPS! execl returned %d, errno: %d\n", result, errno); } else { @@ -947,25 +1206,39 @@ int adb_main(int is_daemon, int server_port) init_transport_registration(); - #if ADB_HOST HOST = 1; usb_vendors_init(); usb_init(); local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT); + adb_auth_init(); char local_name[30]; build_local_name(local_name, sizeof(local_name), server_port); - if(install_listener(local_name, "*smartsocket*", NULL)) { + if(install_listener(local_name, "*smartsocket*", NULL, 0)) { exit(1); } #else + property_get("ro.adb.secure", value, "0"); + auth_enabled = !strcmp(value, "1"); + if (auth_enabled) + adb_auth_init(); + + // Our external storage path may be different than apps, since + // we aren't able to bind mount after dropping root. + const char* adb_external_storage = getenv("ADB_EXTERNAL_STORAGE"); + if (NULL != adb_external_storage) { + setenv("EXTERNAL_STORAGE", adb_external_storage, 1); + } else { + D("Warning: ADB_EXTERNAL_STORAGE is not set. Leaving EXTERNAL_STORAGE" + " unchanged.\n"); + } /* don't listen on a port (default 5037) if running in secure mode */ /* 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); @@ -998,40 +1271,48 @@ int adb_main(int is_daemon, int server_port) 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 { char local_name[30]; build_local_name(local_name, sizeof(local_name), server_port); - if(install_listener(local_name, "*smartsocket*", NULL)) { + if(install_listener(local_name, "*smartsocket*", NULL, 0)) { exit(1); } } - /* for the device, start the usb transport if the - ** android usb device exists and the "service.adb.tcp.port" and - ** "persist.adb.tcp.port" properties are not set. - ** Otherwise start the network transport. - */ + int usb = 0; + if (access(USB_ADB_PATH, F_OK) == 0 || access(USB_FFS_ADB_EP0, F_OK) == 0) { + // listen on USB + usb_init(); + usb = 1; + } + + // If one of these properties is set, also listen on that port + // If one of the properties isn't set and we couldn't listen on usb, + // listen on the default port. property_get("service.adb.tcp.port", value, ""); - if (!value[0]) + if (!value[0]) { property_get("persist.adb.tcp.port", value, ""); + } if (sscanf(value, "%d", &port) == 1 && port > 0) { + printf("using port=%d\n", port); // listen on TCP port specified by service.adb.tcp.port property local_init(port); - } else if (access("/dev/android_adb", F_OK) == 0) { - // listen on USB - usb_init(); - } else { + } else if (!usb) { // listen on default port local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT); } + D("adb_main(): pre init_jdwp()\n"); init_jdwp(); D("adb_main(): post init_jdwp()\n"); @@ -1067,7 +1348,7 @@ void connect_device(char* host, char* buffer, int buffer_size) strncpy(hostbuf, host, sizeof(hostbuf) - 1); if (portstr) { - if ((unsigned int)(portstr - host) >= sizeof(hostbuf)) { + if (portstr - host >= (ptrdiff_t)sizeof(hostbuf)) { snprintf(buffer, buffer_size, "bad host name %s", host); return; } @@ -1201,16 +1482,19 @@ int handle_host_request(char *service, transport_type ttype, char* serial, int r } // return a list of all connected devices - if (!strcmp(service, "devices")) { + if (!strncmp(service, "devices", 7)) { char buffer[4096]; - memset(buf, 0, sizeof(buf)); - memset(buffer, 0, sizeof(buffer)); - D("Getting device list \n"); - list_transports(buffer, sizeof(buffer)); - snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer),buffer); - D("Wrote device list \n"); - writex(reply_fd, buf, strlen(buf)); - return 0; + int use_long = !strcmp(service+7, "-l"); + if (use_long || service[7] == 0) { + memset(buf, 0, sizeof(buf)); + memset(buffer, 0, sizeof(buffer)); + D("Getting device list \n"); + list_transports(buffer, sizeof(buffer), use_long); + snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer),buffer); + D("Wrote device list \n"); + writex(reply_fd, buf, strlen(buf)); + return 0; + } } // add a new TCP transport, device or emulator @@ -1276,6 +1560,16 @@ int handle_host_request(char *service, transport_type ttype, char* serial, int r writex(reply_fd, buf, strlen(buf)); return 0; } + if(!strncmp(service,"get-devpath",strlen("get-devpath"))) { + char *out = "unknown"; + transport = acquire_one_transport(CS_ANY, ttype, serial, NULL); + if (transport && transport->devpath) { + out = transport->devpath; + } + snprintf(buf, sizeof buf, "OKAY%04x%s",(unsigned)strlen(out),out); + writex(reply_fd, buf, strlen(buf)); + return 0; + } // indicates a new emulator instance has started if (!strncmp(service,"emulator:",9)) { int port = atoi(service+9); @@ -1285,24 +1579,63 @@ int handle_host_request(char *service, transport_type ttype, char* serial, int r } #endif // ADB_HOST - if(!strncmp(service,"forward:",8) || !strncmp(service,"killforward:",12)) { + if(!strcmp(service,"list-forward")) { + // Create the list of forward redirections. + char header[9]; + int buffer_size = format_listeners(NULL, 0); + // Add one byte for the trailing zero. + char* buffer = malloc(buffer_size+1); + (void) format_listeners(buffer, buffer_size+1); + snprintf(header, sizeof header, "OKAY%04x", buffer_size); + writex(reply_fd, header, 8); + writex(reply_fd, buffer, buffer_size); + free(buffer); + return 0; + } + + if (!strcmp(service,"killforward-all")) { + remove_all_listeners(); + adb_write(reply_fd, "OKAYOKAY", 8); + return 0; + } + + if(!strncmp(service,"forward:",8) || + !strncmp(service,"killforward:",12)) { char *local, *remote, *err; int r; atransport *transport; int createForward = strncmp(service,"kill",4); + int no_rebind = 0; - local = service + (createForward ? 8 : 12); - remote = strchr(local,';'); - if(remote == 0) { - sendfailmsg(reply_fd, "malformed forward spec"); - return 0; + local = strchr(service, ':') + 1; + + // Handle forward:norebind:<local>... here + if (createForward && !strncmp(local, "norebind:", 9)) { + no_rebind = 1; + local = strchr(local, ':') + 1; } - *remote++ = 0; - if((local[0] == 0) || (remote[0] == 0) || (remote[0] == '*')){ - sendfailmsg(reply_fd, "malformed forward spec"); - return 0; + remote = strchr(local,';'); + + if (createForward) { + // Check forward: parameter format: '<local>;<remote>' + if(remote == 0) { + sendfailmsg(reply_fd, "malformed forward spec"); + return 0; + } + + *remote++ = 0; + if((local[0] == 0) || (remote[0] == 0) || (remote[0] == '*')){ + sendfailmsg(reply_fd, "malformed forward spec"); + return 0; + } + } else { + // Check killforward: parameter format: '<local>' + if (local[0] == 0) { + sendfailmsg(reply_fd, "malformed forward spec"); + return 0; + } } transport = acquire_one_transport(CS_ANY, ttype, serial, &err); @@ -1312,9 +1645,9 @@ int handle_host_request(char *service, transport_type ttype, char* serial, int r } if (createForward) { - r = install_listener(local, remote, transport); + r = install_listener(local, remote, transport, no_rebind); } else { - r = remove_listener(local, remote, transport); + r = remove_listener(local, transport); } if(r == 0) { /* 1st OKAY is connect, 2nd OKAY is status */ @@ -1323,7 +1656,18 @@ int handle_host_request(char *service, transport_type ttype, char* serial, int r } if (createForward) { - sendfailmsg(reply_fd, (r == -1) ? "cannot rebind smartsocket" : "cannot bind socket"); + const char* message; + switch (r) { + case INSTALL_STATUS_CANNOT_BIND: + message = "cannot bind to socket"; + break; + case INSTALL_STATUS_CANNOT_REBIND: + message = "cannot rebind existing socket"; + break; + default: + message = "internal error"; + } + sendfailmsg(reply_fd, message); } else { sendfailmsg(reply_fd, "cannot remove listener"); } @@ -29,13 +29,14 @@ #define A_OKAY 0x59414b4f #define A_CLSE 0x45534c43 #define A_WRTE 0x45545257 +#define A_AUTH 0x48545541 #define A_VERSION 0x01000000 // ADB protocol version #define ADB_VERSION_MAJOR 1 // Used for help/version information #define ADB_VERSION_MINOR 0 // Used for help/version information -#define ADB_SERVER_VERSION 29 // Increment this when we want to force users to start a new adb server +#define ADB_SERVER_VERSION 31 // Increment this when we want to force users to start a new adb server typedef struct amessage amessage; typedef struct apacket apacket; @@ -165,6 +166,8 @@ typedef enum transport_type { kTransportHost, } transport_type; +#define TOKEN_SIZE 20 + struct atransport { atransport *next; @@ -181,6 +184,7 @@ struct atransport int ref_count; unsigned sync_token; int connection_state; + int online; transport_type type; /* usb handle or socket fd as needed */ @@ -190,11 +194,19 @@ struct atransport /* used to identify transports for clients */ char *serial; char *product; + char *model; + char *device; + char *devpath; int adb_port; // Use for emulators (local transport) /* a list of adisconnect callbacks called when the transport is kicked */ int kicked; adisconnect disconnects; + + void *key; + unsigned char token[TOKEN_SIZE]; + fdevent auth_fde; + unsigned failed_auth_attempts; }; @@ -253,7 +265,7 @@ int adb_main(int is_daemon, int server_port); ** get_device_transport does an acquire on your behalf before returning */ void init_transport_registration(void); -int list_transports(char *buf, size_t bufsize); +int list_transports(char *buf, size_t bufsize, int long_listing); void update_transports(void); asocket* create_device_tracker(void); @@ -286,7 +298,7 @@ void register_socket_transport(int s, const char *serial, int port, int local); void unregister_transport(atransport *t); void unregister_all_tcp_transports(); -void register_usb_transport(usb_handle *h, const char *serial, unsigned writeable); +void register_usb_transport(usb_handle *h, const char *serial, const char *devpath, unsigned writeable); /* this should only be used for transports with connection_state == CS_NOPERM */ void unregister_usb_transport(usb_handle *usb); @@ -346,6 +358,7 @@ typedef enum { TRACE_SYSDEPS, TRACE_JDWP, /* 0x100 */ TRACE_SERVICES, + TRACE_AUTH, } AdbTrace; #if ADB_TRACE @@ -405,7 +418,7 @@ void adb_qemu_trace(const char* fmt, ...); #endif -#if !TRACE_PACKETS +#if !DEBUG_PACKETS #define print_packet(tag,p) do {} while (0) #endif @@ -461,6 +474,17 @@ extern int SHELL_EXIT_NOTIFY_FD; #define CHUNK_SIZE (64*1024) +#if !ADB_HOST +#define USB_ADB_PATH "/dev/android_adb" + +#define USB_FFS_ADB_PATH "/dev/usb-ffs/adb/" +#define USB_FFS_ADB_EP(x) USB_FFS_ADB_PATH#x + +#define USB_FFS_ADB_EP0 USB_FFS_ADB_EP(ep0) +#define USB_FFS_ADB_OUT USB_FFS_ADB_EP(ep1) +#define USB_FFS_ADB_IN USB_FFS_ADB_EP(ep2) +#endif + int sendfailmsg(int fd, const char *reason); int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s); diff --git a/adb/adb_auth.h b/adb/adb_auth.h new file mode 100644 index 0000000..1fffa49 --- /dev/null +++ b/adb/adb_auth.h @@ -0,0 +1,54 @@ +/* + * 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. + */ + +#ifndef __ADB_AUTH_H +#define __ADB_AUTH_H + +void adb_auth_init(void); +void adb_auth_verified(atransport *t); + +/* AUTH packets first argument */ +/* Request */ +#define ADB_AUTH_TOKEN 1 +/* Response */ +#define ADB_AUTH_SIGNATURE 2 +#define ADB_AUTH_RSAPUBLICKEY 3 + +#if ADB_HOST + +int adb_auth_sign(void *key, void *token, size_t token_size, void *sig); +void *adb_auth_nextkey(void *current); +int adb_auth_get_userkey(unsigned char *data, size_t len); + +static inline int adb_auth_generate_token(void *token, size_t token_size) { return 0; } +static inline int adb_auth_verify(void *token, void *sig, int siglen) { return 0; } +static inline void adb_auth_confirm_key(unsigned char *data, size_t len, atransport *t) { } +static inline void adb_auth_reload_keys(void) { } + +#else // !ADB_HOST + +static inline int adb_auth_sign(void* key, void *token, size_t token_size, void *sig) { return 0; } +static inline void *adb_auth_nextkey(void *current) { return NULL; } +static inline int adb_auth_get_userkey(unsigned char *data, size_t len) { return 0; } + +int adb_auth_generate_token(void *token, size_t token_size); +int adb_auth_verify(void *token, void *sig, int siglen); +void adb_auth_confirm_key(unsigned char *data, size_t len, atransport *t); +void adb_auth_reload_keys(void); + +#endif // ADB_HOST + +#endif // __ADB_AUTH_H diff --git a/adb/adb_auth_client.c b/adb/adb_auth_client.c new file mode 100644 index 0000000..0b4913e --- /dev/null +++ b/adb/adb_auth_client.c @@ -0,0 +1,245 @@ +/* + * 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 <stdio.h> +#include <string.h> +#include <resolv.h> +#include <cutils/list.h> +#include <cutils/sockets.h> + +#include "sysdeps.h" +#include "adb.h" +#include "adb_auth.h" +#include "fdevent.h" +#include "mincrypt/rsa.h" + +#define TRACE_TAG TRACE_AUTH + + +struct adb_public_key { + struct listnode node; + RSAPublicKey key; +}; + +static struct listnode key_list; + +static char *key_paths[] = { + "/adb_keys", + "/data/misc/adb/adb_keys", + NULL +}; + +static fdevent listener_fde; +static int framework_fd = -1; + + +static void read_keys(const char *file, struct listnode *list) +{ + struct adb_public_key *key; + FILE *f; + char buf[MAX_PAYLOAD]; + char *sep; + int ret; + + f = fopen(file, "r"); + if (!f) { + D("Can't open '%s'\n", file); + return; + } + + while (fgets(buf, sizeof(buf), f)) { + /* Allocate 4 extra bytes to decode the base64 data in-place */ + key = calloc(1, sizeof(*key) + 4); + if (!key) { + D("Can't malloc key\n"); + break; + } + + sep = strpbrk(buf, " \t"); + if (sep) + *sep = '\0'; + + ret = __b64_pton(buf, (u_char *)&key->key, sizeof(key->key) + 4); + if (ret != sizeof(key->key)) { + D("%s: Invalid base64 data ret=%d\n", file, ret); + free(key); + continue; + } + + if (key->key.len != RSANUMWORDS) { + D("%s: Invalid key len %d\n", file, key->key.len); + free(key); + continue; + } + + list_add_tail(list, &key->node); + } + + fclose(f); +} + +static void free_keys(struct listnode *list) +{ + struct listnode *item; + + while (!list_empty(list)) { + item = list_head(list); + list_remove(item); + free(node_to_item(item, struct adb_public_key, node)); + } +} + +void adb_auth_reload_keys(void) +{ + char *path; + char **paths = key_paths; + struct stat buf; + + free_keys(&key_list); + + while ((path = *paths++)) { + if (!stat(path, &buf)) { + D("Loading keys from '%s'\n", path); + read_keys(path, &key_list); + } + } +} + +int adb_auth_generate_token(void *token, size_t token_size) +{ + FILE *f; + int ret; + + f = fopen("/dev/urandom", "r"); + if (!f) + return 0; + + ret = fread(token, token_size, 1, f); + + fclose(f); + return ret * token_size; +} + +int adb_auth_verify(void *token, void *sig, int siglen) +{ + struct listnode *item; + struct adb_public_key *key; + int ret; + + if (siglen != RSANUMBYTES) + return 0; + + list_for_each(item, &key_list) { + key = node_to_item(item, struct adb_public_key, node); + ret = RSA_verify(&key->key, sig, siglen, token); + if (ret) + return 1; + } + + return 0; +} + +static void adb_auth_event(int fd, unsigned events, void *data) +{ + atransport *t = data; + char response[2]; + int ret; + + if (events & FDE_READ) { + ret = unix_read(fd, response, sizeof(response)); + if (ret < 0) { + D("Disconnect"); + fdevent_remove(&t->auth_fde); + framework_fd = -1; + } + else if (ret == 2 && response[0] == 'O' && response[1] == 'K') { + adb_auth_reload_keys(); + adb_auth_verified(t); + } + } +} + +void adb_auth_confirm_key(unsigned char *key, size_t len, atransport *t) +{ + char msg[MAX_PAYLOAD]; + int ret; + + if (framework_fd < 0) { + D("Client not connected\n"); + return; + } + + if (key[len - 1] != '\0') { + D("Key must be a null-terminated string\n"); + return; + } + + ret = snprintf(msg, sizeof(msg), "PK%s", key); + if (ret >= (signed)sizeof(msg)) { + D("Key too long. ret=%d", ret); + return; + } + D("Sending '%s'\n", msg); + + ret = unix_write(framework_fd, msg, ret); + if (ret < 0) { + D("Failed to write PK, errno=%d\n", errno); + return; + } + + fdevent_install(&t->auth_fde, framework_fd, adb_auth_event, t); + fdevent_add(&t->auth_fde, FDE_READ); +} + +static void adb_auth_listener(int fd, unsigned events, void *data) +{ + struct sockaddr addr; + socklen_t alen; + int s; + + alen = sizeof(addr); + + s = adb_socket_accept(fd, &addr, &alen); + if (s < 0) { + D("Failed to accept: errno=%d\n", errno); + return; + } + + framework_fd = s; +} + +void adb_auth_init(void) +{ + int fd, ret; + + list_init(&key_list); + adb_auth_reload_keys(); + + fd = android_get_control_socket("adbd"); + if (fd < 0) { + D("Failed to get adbd socket\n"); + return; + } + + ret = listen(fd, 4); + if (ret < 0) { + D("Failed to listen on '%d'\n", fd); + return; + } + + fdevent_install(&listener_fde, fd, adb_auth_listener, NULL); + fdevent_add(&listener_fde, FDE_READ); +} diff --git a/adb/adb_auth_host.c b/adb/adb_auth_host.c new file mode 100644 index 0000000..9039d42 --- /dev/null +++ b/adb/adb_auth_host.c @@ -0,0 +1,426 @@ +/* + * 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 <stdio.h> + +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN +# include "windows.h" +# include "shlobj.h" +#else +# include <sys/types.h> +# include <sys/stat.h> +# include <unistd.h> +#endif +#include <string.h> + +#include "sysdeps.h" +#include "adb.h" +#include "adb_auth.h" + +/* HACK: we need the RSAPublicKey struct + * but RSA_verify conflits with openssl */ +#define RSA_verify RSA_verify_mincrypt +#include "mincrypt/rsa.h" +#undef RSA_verify + +#include <cutils/list.h> + +#include <openssl/evp.h> +#include <openssl/objects.h> +#include <openssl/pem.h> +#include <openssl/rsa.h> +#include <openssl/sha.h> + +#define TRACE_TAG TRACE_AUTH + +#define ANDROID_PATH ".android" +#define ADB_KEY_FILE "adbkey" + + +struct adb_private_key { + struct listnode node; + RSA *rsa; +}; + +static struct listnode key_list; + + +/* Convert OpenSSL RSA private key to android pre-computed RSAPublicKey format */ +static int RSA_to_RSAPublicKey(RSA *rsa, RSAPublicKey *pkey) +{ + int ret = 1; + unsigned int i; + + BN_CTX* ctx = BN_CTX_new(); + BIGNUM* r32 = BN_new(); + BIGNUM* rr = BN_new(); + BIGNUM* r = BN_new(); + BIGNUM* rem = BN_new(); + BIGNUM* n = BN_new(); + BIGNUM* n0inv = BN_new(); + + if (RSA_size(rsa) != RSANUMBYTES) { + ret = 0; + goto out; + } + + BN_set_bit(r32, 32); + BN_copy(n, rsa->n); + BN_set_bit(r, RSANUMWORDS * 32); + BN_mod_sqr(rr, r, n, ctx); + BN_div(NULL, rem, n, r32, ctx); + BN_mod_inverse(n0inv, rem, r32, ctx); + + pkey->len = RSANUMWORDS; + pkey->n0inv = 0 - BN_get_word(n0inv); + for (i = 0; i < RSANUMWORDS; i++) { + BN_div(rr, rem, rr, r32, ctx); + pkey->rr[i] = BN_get_word(rem); + BN_div(n, rem, n, r32, ctx); + pkey->n[i] = BN_get_word(rem); + } + pkey->exponent = BN_get_word(rsa->e); + +out: + BN_free(n0inv); + BN_free(n); + BN_free(rem); + BN_free(r); + BN_free(rr); + BN_free(r32); + BN_CTX_free(ctx); + + return ret; +} + +static void get_user_info(char *buf, size_t len) +{ + char hostname[1024], username[1024]; + int ret; + +#ifndef _WIN32 + ret = gethostname(hostname, sizeof(hostname)); + if (ret < 0) +#endif + strcpy(hostname, "unknown"); + +#if !defined _WIN32 && !defined ADB_HOST_ON_TARGET + ret = getlogin_r(username, sizeof(username)); + if (ret < 0) +#endif + strcpy(username, "unknown"); + + ret = snprintf(buf, len, " %s@%s", username, hostname); + if (ret >= (signed)len) + buf[len - 1] = '\0'; +} + +static int write_public_keyfile(RSA *private_key, const char *private_key_path) +{ + RSAPublicKey pkey; + BIO *bio, *b64, *bfile; + char path[PATH_MAX], info[MAX_PAYLOAD]; + int ret; + + ret = snprintf(path, sizeof(path), "%s.pub", private_key_path); + if (ret >= (signed)sizeof(path)) + return 0; + + ret = RSA_to_RSAPublicKey(private_key, &pkey); + if (!ret) { + D("Failed to convert to publickey\n"); + return 0; + } + + bfile = BIO_new_file(path, "w"); + if (!bfile) { + D("Failed to open '%s'\n", path); + return 0; + } + + D("Writing public key to '%s'\n", path); + + b64 = BIO_new(BIO_f_base64()); + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + + bio = BIO_push(b64, bfile); + BIO_write(bio, &pkey, sizeof(pkey)); + BIO_flush(bio); + BIO_pop(b64); + BIO_free(b64); + + get_user_info(info, sizeof(info)); + BIO_write(bfile, info, strlen(info)); + BIO_flush(bfile); + BIO_free_all(bfile); + + return 1; +} + +static int generate_key(const char *file) +{ + EVP_PKEY* pkey = EVP_PKEY_new(); + BIGNUM* exponent = BN_new(); + RSA* rsa = RSA_new(); + mode_t old_mask; + FILE *f = NULL; + int ret = 0; + + D("generate_key '%s'\n", file); + + if (!pkey || !exponent || !rsa) { + D("Failed to allocate key\n"); + goto out; + } + + BN_set_word(exponent, RSA_F4); + RSA_generate_key_ex(rsa, 2048, exponent, NULL); + EVP_PKEY_set1_RSA(pkey, rsa); + + old_mask = umask(077); + + f = fopen(file, "w"); + if (!f) { + D("Failed to open '%s'\n", file); + umask(old_mask); + goto out; + } + + umask(old_mask); + + if (!PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL)) { + D("Failed to write key\n"); + goto out; + } + + if (!write_public_keyfile(rsa, file)) { + D("Failed to write public key\n"); + goto out; + } + + ret = 1; + +out: + if (f) + fclose(f); + EVP_PKEY_free(pkey); + RSA_free(rsa); + BN_free(exponent); + return ret; +} + +static int read_key(const char *file, struct listnode *list) +{ + struct adb_private_key *key; + FILE *f; + + D("read_key '%s'\n", file); + + f = fopen(file, "r"); + if (!f) { + D("Failed to open '%s'\n", file); + return 0; + } + + key = malloc(sizeof(*key)); + if (!key) { + D("Failed to alloc key\n"); + fclose(f); + return 0; + } + key->rsa = RSA_new(); + + if (!PEM_read_RSAPrivateKey(f, &key->rsa, NULL, NULL)) { + D("Failed to read key\n"); + fclose(f); + RSA_free(key->rsa); + free(key); + return 0; + } + + fclose(f); + list_add_tail(list, &key->node); + return 1; +} + +static int get_user_keyfilepath(char *filename, size_t len) +{ + const char *format, *home; + char android_dir[PATH_MAX]; + struct stat buf; +#ifdef _WIN32 + char path[PATH_MAX]; + home = getenv("ANDROID_SDK_HOME"); + if (!home) { + SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, 0, path); + home = path; + } + format = "%s\\%s"; +#else + home = getenv("HOME"); + if (!home) + return -1; + format = "%s/%s"; +#endif + + D("home '%s'\n", home); + + if (snprintf(android_dir, sizeof(android_dir), format, home, + ANDROID_PATH) >= (int)sizeof(android_dir)) + return -1; + + if (stat(android_dir, &buf)) { + if (adb_mkdir(android_dir, 0750) < 0) { + D("Cannot mkdir '%s'", android_dir); + return -1; + } + } + + return snprintf(filename, len, format, android_dir, ADB_KEY_FILE); +} + +static int get_user_key(struct listnode *list) +{ + struct stat buf; + char path[PATH_MAX]; + int ret; + + ret = get_user_keyfilepath(path, sizeof(path)); + if (ret < 0 || ret >= (signed)sizeof(path)) { + D("Error getting user key filename"); + return 0; + } + + D("user key '%s'\n", path); + + if (stat(path, &buf) == -1) { + if (!generate_key(path)) { + D("Failed to generate new key\n"); + return 0; + } + } + + return read_key(path, list); +} + +static void get_vendor_keys(struct listnode *list) +{ + const char *adb_keys_path; + char keys_path[MAX_PAYLOAD]; + char *path; + char *save; + struct stat buf; + + adb_keys_path = getenv("ADB_VENDOR_KEYS"); + if (!adb_keys_path) + return; + strncpy(keys_path, adb_keys_path, sizeof(keys_path)); + + path = adb_strtok_r(keys_path, ENV_PATH_SEPARATOR_STR, &save); + while (path) { + D("Reading: '%s'\n", path); + + if (stat(path, &buf)) + D("Can't read '%s'\n", path); + else if (!read_key(path, list)) + D("Failed to read '%s'\n", path); + + path = adb_strtok_r(NULL, ENV_PATH_SEPARATOR_STR, &save); + } +} + +int adb_auth_sign(void *node, void *token, size_t token_size, void *sig) +{ + unsigned int len; + struct adb_private_key *key = node_to_item(node, struct adb_private_key, node); + + if (!RSA_sign(NID_sha1, token, token_size, sig, &len, key->rsa)) { + return 0; + } + + D("adb_auth_sign len=%d\n", len); + return (int)len; +} + +void *adb_auth_nextkey(void *current) +{ + struct listnode *item; + + if (list_empty(&key_list)) + return NULL; + + if (!current) + return list_head(&key_list); + + list_for_each(item, &key_list) { + if (item == current) { + /* current is the last item, we tried all the keys */ + if (item->next == &key_list) + return NULL; + return item->next; + } + } + + return NULL; +} + +int adb_auth_get_userkey(unsigned char *data, size_t len) +{ + char path[PATH_MAX]; + char *file; + int ret; + + ret = get_user_keyfilepath(path, sizeof(path) - 4); + if (ret < 0 || ret >= (signed)(sizeof(path) - 4)) { + D("Error getting user key filename"); + return 0; + } + strcat(path, ".pub"); + + file = load_file(path, (unsigned*)&ret); + if (!file) { + D("Can't load '%s'\n", path); + return 0; + } + + if (len < (size_t)(ret + 1)) { + D("%s: Content too large ret=%d\n", path, ret); + return 0; + } + + memcpy(data, file, ret); + data[ret] = '\0'; + + return ret + 1; +} + +void adb_auth_init(void) +{ + int ret; + + D("adb_auth_init\n"); + + list_init(&key_list); + + ret = get_user_key(&key_list); + if (!ret) { + D("Failed to get user key\n"); + return; + } + + get_vendor_keys(&key_list); +} diff --git a/adb/adb_client.c b/adb/adb_client.c index 9a812f0..8340738 100644 --- a/adb/adb_client.c +++ b/adb/adb_client.c @@ -17,6 +17,7 @@ static transport_type __adb_transport = kTransportAny; static const char* __adb_serial = NULL; static int __adb_server_port = DEFAULT_ADB_PORT; +static const char* __adb_server_name = NULL; void adb_set_transport(transport_type type, const char* serial) { @@ -29,6 +30,11 @@ void adb_set_tcp_specifics(int server_port) __adb_server_port = server_port; } +void adb_set_tcp_name(const char* hostname) +{ + __adb_server_name = hostname; +} + int adb_get_emulator_console_port(void) { const char* serial = __adb_serial; @@ -181,7 +187,11 @@ int _adb_connect(const char *service) } snprintf(tmp, sizeof tmp, "%04x", len); - fd = socket_loopback_client(__adb_server_port, SOCK_STREAM); + if (__adb_server_name) + fd = socket_network_client(__adb_server_name, __adb_server_port, SOCK_STREAM); + else + fd = socket_loopback_client(__adb_server_port, SOCK_STREAM); + if(fd < 0) { strcpy(__adb_error, "cannot connect to daemon"); return -2; @@ -212,7 +222,10 @@ int adb_connect(const char *service) int fd = _adb_connect("host:version"); D("adb_connect: service %s\n", service); - if(fd == -2) { + if(fd == -2 && __adb_server_name) { + fprintf(stderr,"** Cannot start server on remote host\n"); + return fd; + } else if(fd == -2) { fprintf(stdout,"* daemon not running. starting it now on port %d *\n", __adb_server_port); start_server: @@ -266,7 +279,7 @@ int adb_connect(const char *service) fd = _adb_connect(service); if(fd == -2) { - fprintf(stderr,"** daemon still not running"); + fprintf(stderr,"** daemon still not running\n"); } D("adb_connect: return fd %d\n", fd); diff --git a/adb/adb_client.h b/adb/adb_client.h index 40ab189..0ec47ca 100644 --- a/adb/adb_client.h +++ b/adb/adb_client.h @@ -29,6 +29,10 @@ void adb_set_transport(transport_type type, const char* serial); */ void adb_set_tcp_specifics(int server_port); +/* Set TCP Hostname of the transport to use +*/ +void adb_set_tcp_name(const char* hostname); + /* Return the console port of the currently connected emulator (if any) * of -1 if there is no emulator, and -2 if there is more than one. * assumes adb_set_transport() was alled previously... diff --git a/adb/commandline.c b/adb/commandline.c index d2b8166..a927423 100644 --- a/adb/commandline.c +++ b/adb/commandline.c @@ -46,6 +46,7 @@ int install_app(transport_type transport, char* serial, int argc, char** argv); int uninstall_app(transport_type transport, char* serial, int argc, char** argv); static const char *gProductOutPath = NULL; +extern int gListenAll; static char *product_file(const char *extra) { @@ -80,12 +81,13 @@ void help() fprintf(stderr, "\n" + " -a - directs adb to listen on all interfaces for a connection\n" " -d - directs command to the only connected USB device\n" " returns an error if more than one USB device is present.\n" " -e - directs command to the only running emulator.\n" " returns an error if more than one emulator is running.\n" - " -s <serial number> - directs command to the USB device or emulator with\n" - " the given serial number. Overrides ANDROID_SERIAL\n" + " -s <specific device> - directs command to the device or emulator with the given\n" + " serial number or qualifier. Overrides ANDROID_SERIAL\n" " environment variable.\n" " -p <product name or path> - simple product name like 'sooner', or\n" " a relative/absolute path to a product\n" @@ -93,7 +95,10 @@ void help() " If -p is not specified, the ANDROID_PRODUCT_OUT\n" " environment variable is used, which must\n" " be an absolute path.\n" - " devices - list all connected devices\n" + " -H - Name of adb server host (default: localhost)\n" + " -P - Port of adb server (default: 5037)\n" + " devices [-l] - list all connected devices\n" + " ('-l' will also list device qualifiers)\n" " connect <host>[:<port>] - connect to a device via TCP/IP\n" " Port 5555 is used by default if no port number is specified.\n" " disconnect [<host>[:<port>]] - disconnect from a TCP/IP device.\n" @@ -111,6 +116,9 @@ void help() " adb shell <command> - run remote shell command\n" " adb emu <command> - run emulator console command\n" " adb logcat [ <filter-spec> ] - View device log\n" + " adb forward --list - list all forward socket connections.\n" + " the format is a list of lines with the following format:\n" + " <serial> \" \" <local> \" \" <remote> \"\\n\"\n" " adb forward <local> <remote> - forward socket connections\n" " forward specs are one of: \n" " tcp:<port>\n" @@ -119,6 +127,11 @@ void help() " localfilesystem:<unix domain socket name>\n" " dev:<character device name>\n" " jdwp:<process pid> (remote only)\n" + " adb forward --no-rebind <local> <remote>\n" + " - same as 'adb forward <local> <remote>' but fails\n" + " if <local> is already forwarded\n" + " adb forward --remove <local> - remove a specific forward socket connection\n" + " adb forward --remove-all - remove all forward socket connections\n" " adb jdwp - list PIDs of processes hosting a JDWP transport\n" " adb install [-l] [-r] [-s] [--algo <algorithm name> --key <hex-encoded key> --iv <hex-encoded iv>] <file>\n" " - push this package file to the device and install it\n" @@ -159,6 +172,7 @@ void help() " adb kill-server - kill the server if it is running\n" " adb get-state - prints: offline | bootloader | device\n" " adb get-serialno - prints: <serial-number>\n" + " adb get-devpath - prints: <device-path>\n" " adb status-window - continuously print device status for a specified device\n" " adb remount - remounts the /system partition on the device read-write\n" " adb reboot [bootloader|recovery] - reboots the device, optionally into the bootloader or recovery program\n" @@ -369,7 +383,7 @@ static void format_host_command(char* buffer, size_t buflen, const char* comman } } -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]; @@ -405,7 +419,7 @@ int adb_download_buffer(const char *service, const void* data, int sz, 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); } } @@ -437,11 +451,11 @@ int adb_download(const char *service, const char *fn, unsigned progress) 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; } @@ -936,9 +950,9 @@ int adb_commandline(int argc, char **argv) int server_port = DEFAULT_ADB_PORT; if (server_port_str && strlen(server_port_str) > 0) { server_port = (int) strtol(server_port_str, NULL, 0); - if (server_port <= 0) { + if (server_port <= 0 || server_port > 65535) { fprintf(stderr, - "adb: Env var ANDROID_ADB_SERVER_PORT must be a positive number. Got \"%s\"\n", + "adb: Env var ANDROID_ADB_SERVER_PORT must be a positive number less than 65535. Got \"%s\"\n", server_port_str); return usage(); } @@ -984,6 +998,42 @@ int adb_commandline(int argc, char **argv) ttype = kTransportUsb; } else if (!strcmp(argv[0],"-e")) { ttype = kTransportLocal; + } else if (!strcmp(argv[0],"-a")) { + gListenAll = 1; + } else if(!strncmp(argv[0], "-H", 2)) { + const char *hostname = NULL; + if (argv[0][2] == '\0') { + if (argc < 2) return usage(); + hostname = argv[1]; + argc--; + argv++; + } else { + hostname = argv[0] + 2; + } + adb_set_tcp_name(hostname); + + } else if(!strncmp(argv[0], "-P", 2)) { + if (argv[0][2] == '\0') { + if (argc < 2) return usage(); + server_port_str = argv[1]; + argc--; + argv++; + } else { + server_port_str = argv[0] + 2; + } + if (strlen(server_port_str) > 0) { + server_port = (int) strtol(server_port_str, NULL, 0); + if (server_port <= 0 || server_port > 65535) { + fprintf(stderr, + "adb: port number must be a positive number less than 65536. Got \"%s\"\n", + server_port_str); + return usage(); + } + } else { + fprintf(stderr, + "adb: port number must be a positive number less than 65536. Got empty string.\n"); + return usage(); + } } else { /* out of recognized modifiers and flags */ break; @@ -1016,7 +1066,16 @@ top: if(!strcmp(argv[0], "devices")) { char *tmp; - snprintf(buf, sizeof buf, "host:%s", argv[0]); + char *listopt; + if (argc < 2) + listopt = ""; + else if (argc == 2 && !strcmp(argv[1], "-l")) + listopt = argv[1]; + else { + fprintf(stderr, "Usage: adb devices [-l]\n"); + return 1; + } + snprintf(buf, sizeof buf, "host:%s%s", argv[0], listopt); tmp = adb_query(buf); if(tmp) { printf("List of devices attached \n"); @@ -1212,16 +1271,85 @@ top: } if(!strcmp(argv[0], "forward")) { - if(argc != 3) return usage(); + char host_prefix[64]; + char remove = 0; + char remove_all = 0; + char list = 0; + char no_rebind = 0; + + // Parse options here. + while (argc > 1 && argv[1][0] == '-') { + if (!strcmp(argv[1], "--list")) + list = 1; + else if (!strcmp(argv[1], "--remove")) + remove = 1; + else if (!strcmp(argv[1], "--remove-all")) + remove_all = 1; + else if (!strcmp(argv[1], "--no-rebind")) + no_rebind = 1; + else { + return usage(); + } + argc--; + argv++; + } + + // Ensure we can only use one option at a time. + if (list + remove + remove_all + no_rebind > 1) { + return usage(); + } + + // Determine the <host-prefix> for this command. if (serial) { - snprintf(buf, sizeof buf, "host-serial:%s:forward:%s;%s",serial, argv[1], argv[2]); + snprintf(host_prefix, sizeof host_prefix, "host-serial:%s", + serial); } else if (ttype == kTransportUsb) { - snprintf(buf, sizeof buf, "host-usb:forward:%s;%s", argv[1], argv[2]); + snprintf(host_prefix, sizeof host_prefix, "host-usb"); } else if (ttype == kTransportLocal) { - snprintf(buf, sizeof buf, "host-local:forward:%s;%s", argv[1], argv[2]); + snprintf(host_prefix, sizeof host_prefix, "host-local"); } else { - snprintf(buf, sizeof buf, "host:forward:%s;%s", argv[1], argv[2]); + snprintf(host_prefix, sizeof host_prefix, "host"); + } + + // Implement forward --list + if (list) { + if (argc != 1) + return usage(); + snprintf(buf, sizeof buf, "%s:list-forward", host_prefix); + char* forwards = adb_query(buf); + if (forwards == NULL) { + fprintf(stderr, "error: %s\n", adb_error()); + return 1; + } + printf("%s", forwards); + free(forwards); + return 0; + } + + // Implement forward --remove-all + else if (remove_all) { + if (argc != 1) + return usage(); + snprintf(buf, sizeof buf, "%s:killforward-all", host_prefix); } + + // Implement forward --remove <local> + else if (remove) { + if (argc != 2) + return usage(); + snprintf(buf, sizeof buf, "%s:killforward:%s", host_prefix, argv[1]); + } + // Or implement one of: + // forward <local> <remote> + // forward --no-rebind <local> <remote> + else + { + if (argc != 3) + return usage(); + const char* command = no_rebind ? "forward:norebind:" : "forward"; + snprintf(buf, sizeof buf, "%s:%s:%s;%s", host_prefix, command, argv[1], argv[2]); + } + if(adb_command(buf)) { fprintf(stderr,"error: %s\n", adb_error()); return 1; @@ -1298,7 +1426,8 @@ top: /* passthrough commands */ if(!strcmp(argv[0],"get-state") || - !strcmp(argv[0],"get-serialno")) + !strcmp(argv[0],"get-serialno") || + !strcmp(argv[0],"get-devpath")) { char *tmp; diff --git a/adb/protocol.txt b/adb/protocol.txt index 398d042..c9d3c24 100644 --- a/adb/protocol.txt +++ b/adb/protocol.txt @@ -72,7 +72,25 @@ large maxdata value, the connection with the other side must be closed. The system identity string should be "<systemtype>:<serialno>:<banner>" where systemtype is "bootloader", "device", or "host", serialno is some kind of unique ID (or empty), and banner is a human-readable version -or identifier string (informational only). +or identifier string. The banner is used to transmit useful properties. + + +--- AUTH(type, 0, "data") ---------------------------------------------- + +The AUTH message informs the recipient that authentication is required to +connect to the sender. If type is TOKEN(1), data is a random token that +the recipient can sign with a private key. The recipient replies with an +AUTH packet where type is SIGNATURE(2) and data is the signature. If the +signature verification succeeds, the sender replies with a CONNECT packet. + +If the signature verification fails, the sender replies with a new AUTH +packet and a new random token, so that the recipient can retry signing +with a different private key. + +Once the recipient has tried all its private keys, it can reply with an +AUTH packet where type is RSAPUBLICKEY(3) and data is the public key. If +possible, an on-screen confirmation may be displayed for the user to +confirm they want to install the public key on the device. --- OPEN(local-id, 0, "destination") ----------------------------------- @@ -166,6 +184,7 @@ to send across the wire. #define A_SYNC 0x434e5953 #define A_CNXN 0x4e584e43 +#define A_AUTH 0x48545541 #define A_OPEN 0x4e45504f #define A_OKAY 0x59414b4f #define A_CLSE 0x45534c43 diff --git a/adb/services.c b/adb/services.c index 495a083..54d21a8 100644 --- a/adb/services.c +++ b/adb/services.c @@ -202,7 +202,7 @@ static void echo_service(int fd, void *cookie) int c; for(;;) { - r = read(fd, buf, 4096); + r = adb_read(fd, buf, 4096); if(r == 0) goto done; if(r < 0) { if(errno == EINTR) continue; diff --git a/adb/sockets.c b/adb/sockets.c index cd31b23..305cb44 100644 --- a/adb/sockets.c +++ b/adb/sockets.c @@ -608,12 +608,30 @@ unsigned unhex(unsigned char *s, int len) return n; } +#define PREFIX(str) { str, sizeof(str) - 1 } +static const struct prefix_struct { + const char *str; + const size_t len; +} prefixes[] = { + PREFIX("usb:"), + PREFIX("product:"), + PREFIX("model:"), + PREFIX("device:"), +}; +static const int num_prefixes = (sizeof(prefixes) / sizeof(prefixes[0])); + /* skip_host_serial return the position in a string skipping over the 'serial' parameter in the ADB protocol, where parameter string may be a host:port string containing the protocol delimiter (colon). */ char *skip_host_serial(char *service) { char *first_colon, *serial_end; + int i; + + for (i = 0; i < num_prefixes; i++) { + if (!strncmp(service, prefixes[i].str, prefixes[i].len)) + return strchr(service + prefixes[i].len, ':'); + } first_colon = strchr(service, ':'); if (!first_colon) { diff --git a/adb/sysdeps.h b/adb/sysdeps.h index b518076..0252ef3 100644 --- a/adb/sysdeps.h +++ b/adb/sysdeps.h @@ -38,6 +38,7 @@ #define OS_PATH_SEPARATOR '\\' #define OS_PATH_SEPARATOR_STR "\\" +#define ENV_PATH_SEPARATOR_STR ";" typedef CRITICAL_SECTION adb_mutex_t; @@ -254,6 +255,8 @@ static __inline__ int adb_is_absolute_host_path( const char* path ) return isalpha(path[0]) && path[1] == ':' && path[2] == '\\'; } +extern char* adb_strtok_r(char *str, const char *delim, char **saveptr); + #else /* !_WIN32 a.k.a. Unix */ #include "fdevent.h" @@ -272,9 +275,26 @@ static __inline__ int adb_is_absolute_host_path( const char* path ) #include <netinet/in.h> #include <netinet/tcp.h> #include <string.h> +#include <unistd.h> + +/* + * TEMP_FAILURE_RETRY is defined by some, but not all, versions of + * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's + * not already defined, then define it here. + */ +#ifndef TEMP_FAILURE_RETRY +/* Used to retry syscalls that can return EINTR. */ +#define TEMP_FAILURE_RETRY(exp) ({ \ + typeof (exp) _rc; \ + do { \ + _rc = (exp); \ + } while (_rc == -1 && errno == EINTR); \ + _rc; }) +#endif #define OS_PATH_SEPARATOR '/' #define OS_PATH_SEPARATOR_STR "/" +#define ENV_PATH_SEPARATOR_STR ":" typedef pthread_mutex_t adb_mutex_t; @@ -306,7 +326,7 @@ static __inline__ int unix_open(const char* path, int options,...) { if ((options & O_CREAT) == 0) { - return open(path, options); + return TEMP_FAILURE_RETRY( open(path, options) ); } else { @@ -315,19 +335,19 @@ static __inline__ int unix_open(const char* path, int options,...) va_start( args, options ); mode = va_arg( args, int ); va_end( args ); - return open(path, options, mode); + return TEMP_FAILURE_RETRY( open( path, options, mode ) ); } } static __inline__ int adb_open_mode( const char* pathname, int options, int mode ) { - return open( pathname, options, mode ); + return TEMP_FAILURE_RETRY( open( pathname, options, mode ) ); } static __inline__ int adb_open( const char* pathname, int options ) { - int fd = open( pathname, options ); + int fd = TEMP_FAILURE_RETRY( open( pathname, options ) ); if (fd < 0) return -1; close_on_exec( fd ); @@ -353,7 +373,7 @@ static __inline__ int adb_close(int fd) static __inline__ int adb_read(int fd, void* buf, size_t len) { - return read(fd, buf, len); + return TEMP_FAILURE_RETRY( read( fd, buf, len ) ); } #undef read @@ -361,7 +381,7 @@ static __inline__ int adb_read(int fd, void* buf, size_t len) static __inline__ int adb_write(int fd, const void* buf, size_t len) { - return write(fd, buf, len); + return TEMP_FAILURE_RETRY( write( fd, buf, len ) ); } #undef write #define write ___xxx_write @@ -382,7 +402,7 @@ static __inline__ int adb_unlink(const char* path) static __inline__ int adb_creat(const char* path, int mode) { - int fd = creat(path, mode); + int fd = TEMP_FAILURE_RETRY( creat( path, mode ) ); if ( fd < 0 ) return -1; @@ -397,7 +417,7 @@ static __inline__ int adb_socket_accept(int serverfd, struct sockaddr* addr, { int fd; - fd = accept(serverfd, addr, addrlen); + fd = TEMP_FAILURE_RETRY( accept( serverfd, addr, addrlen ) ); if (fd >= 0) close_on_exec(fd); @@ -490,6 +510,13 @@ static __inline__ int adb_is_absolute_host_path( const char* path ) return path[0] == '/'; } +static __inline__ char* adb_strtok_r(char *str, const char *delim, char **saveptr) +{ + return strtok_r(str, delim, saveptr); +} +#undef strtok_r +#define strtok_r ___xxx_strtok_r + #endif /* !_WIN32 */ #endif /* _ADB_SYSDEPS_H */ diff --git a/adb/sysdeps_win32.c b/adb/sysdeps_win32.c index c426718..2105b16 100644 --- a/adb/sysdeps_win32.c +++ b/adb/sysdeps_win32.c @@ -781,7 +781,7 @@ int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrle 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; @@ -2140,3 +2140,81 @@ adb_sysdeps_init( void ) InitializeCriticalSection( &_win32_lock ); } +/* Windows doesn't have strtok_r. Use the one from bionic. */ + +/* + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * 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 University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +char * +adb_strtok_r(char *s, const char *delim, char **last) +{ + char *spanp; + int c, sc; + char *tok; + + + if (s == NULL && (s = *last) == NULL) + return (NULL); + + /* + * Skip (span) leading delimiters (s += strspn(s, delim), sort of). + */ +cont: + c = *s++; + for (spanp = (char *)delim; (sc = *spanp++) != 0;) { + if (c == sc) + goto cont; + } + + if (c == 0) { /* no non-delimiter characters */ + *last = NULL; + return (NULL); + } + tok = s - 1; + + /* + * Scan token (scan for delimiters: s += strcspn(s, delim), sort of). + * Note that delim must have one NUL; we stop if we see that, too. + */ + for (;;) { + c = *s++; + spanp = (char *)delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + *last = s; + return (tok); + } + } while (sc != 0); + } + /* NOTREACHED */ +} diff --git a/adb/transport.c b/adb/transport.c index 2f7bd27..9fd6cc2 100644 --- a/adb/transport.c +++ b/adb/transport.c @@ -370,7 +370,7 @@ static int list_transports_msg(char* buffer, size_t bufferlen) char head[5]; int len; - len = list_transports(buffer+4, bufferlen-4); + len = list_transports(buffer+4, bufferlen-4, 0); snprintf(head, sizeof(head), "%04x", len); memcpy(buffer, head, 4); len += 4; @@ -601,6 +601,12 @@ static void transport_registration_func(int _fd, unsigned ev, void *data) free(t->product); if (t->serial) free(t->serial); + if (t->model) + free(t->model); + if (t->device) + free(t->device); + if (t->devpath) + free(t->devpath); memset(t,0xee,sizeof(atransport)); free(t); @@ -737,6 +743,45 @@ void remove_transport_disconnect(atransport* t, adisconnect* dis) dis->next = dis->prev = dis; } +static int qual_char_is_invalid(char ch) +{ + if ('A' <= ch && ch <= 'Z') + return 0; + if ('a' <= ch && ch <= 'z') + return 0; + if ('0' <= ch && ch <= '9') + return 0; + return 1; +} + +static int qual_match(const char *to_test, + const char *prefix, const char *qual, int sanitize_qual) +{ + if (!to_test || !*to_test) + /* Return true if both the qual and to_test are null strings. */ + return !qual || !*qual; + + if (!qual) + return 0; + + if (prefix) { + while (*prefix) { + if (*prefix++ != *to_test++) + return 0; + } + } + + while (*qual) { + char ch = *qual++; + if (sanitize_qual && qual_char_is_invalid(ch)) + ch = '_'; + if (ch != *to_test++) + return 0; + } + + /* Everything matched so far. Return true if *to_test is a NUL. */ + return !*to_test; +} atransport *acquire_one_transport(int state, transport_type ttype, const char* serial, char** error_out) { @@ -758,9 +803,19 @@ retry: /* check for matching serial number */ if (serial) { - if (t->serial && !strcmp(serial, t->serial)) { + if ((t->serial && !strcmp(serial, t->serial)) || + (t->devpath && !strcmp(serial, t->devpath)) || + qual_match(serial, "product:", t->product, 0) || + qual_match(serial, "model:", t->model, 1) || + qual_match(serial, "device:", t->device, 0)) { + if (result) { + if (error_out) + *error_out = "more than one device"; + ambiguous = 1; + result = NULL; + break; + } result = t; - break; } } else { if (ttype == kTransportUsb && t->type == kTransportUsb) { @@ -837,7 +892,58 @@ static const char *statename(atransport *t) } } -int list_transports(char *buf, size_t bufsize) +static void add_qual(char **buf, size_t *buf_size, + const char *prefix, const char *qual, int sanitize_qual) +{ + size_t len; + int prefix_len; + + if (!buf || !*buf || !buf_size || !*buf_size || !qual || !*qual) + return; + + len = snprintf(*buf, *buf_size, "%s%n%s", prefix, &prefix_len, qual); + + if (sanitize_qual) { + char *cp; + for (cp = *buf + prefix_len; cp < *buf + len; cp++) { + if (qual_char_is_invalid(*cp)) + *cp = '_'; + } + } + + *buf_size -= len; + *buf += len; +} + +static size_t format_transport(atransport *t, char *buf, size_t bufsize, + int long_listing) +{ + const char* serial = t->serial; + if (!serial || !serial[0]) + serial = "????????????"; + + if (!long_listing) { + return snprintf(buf, bufsize, "%s\t%s\n", serial, statename(t)); + } else { + size_t len, remaining = bufsize; + + len = snprintf(buf, remaining, "%-22s %s", serial, statename(t)); + remaining -= len; + buf += len; + + add_qual(&buf, &remaining, " ", t->devpath, 0); + add_qual(&buf, &remaining, " product:", t->product, 0); + add_qual(&buf, &remaining, " model:", t->model, 1); + add_qual(&buf, &remaining, " device:", t->device, 0); + + len = snprintf(buf, remaining, "\n"); + remaining -= len; + + return bufsize - remaining; + } +} + +int list_transports(char *buf, size_t bufsize, int long_listing) { char* p = buf; char* end = buf + bufsize; @@ -847,11 +953,7 @@ int list_transports(char *buf, size_t bufsize) /* XXX OVERRUN PROBLEMS XXX */ adb_mutex_lock(&transport_lock); for(t = transport_list.next; t != &transport_list; t = t->next) { - const char* serial = t->serial; - if (!serial || !serial[0]) - serial = "????????????"; - len = snprintf(p, end - p, "%s\t%s\n", serial, statename(t)); - + len = format_transport(t, p, end - p, long_listing); if (p + len >= end) { /* discard last line if buffer is too short */ break; @@ -956,7 +1058,7 @@ void unregister_all_tcp_transports() #endif -void register_usb_transport(usb_handle *usb, const char *serial, unsigned writeable) +void register_usb_transport(usb_handle *usb, const char *serial, const char *devpath, unsigned writeable) { atransport *t = calloc(1, sizeof(atransport)); D("transport: %p init'ing for usb_handle %p (sn='%s')\n", t, usb, @@ -965,6 +1067,9 @@ void register_usb_transport(usb_handle *usb, const char *serial, unsigned writea if(serial) { t->serial = strdup(serial); } + if(devpath) { + t->devpath = strdup(devpath); + } register_transport(t); } diff --git a/adb/usb_libusb.c b/adb/usb_libusb.c index 8c75266..06ff5dc 100644 --- a/adb/usb_libusb.c +++ b/adb/usb_libusb.c @@ -347,7 +347,7 @@ register_device(struct usb_handle *uh, const char *serial) adb_mutex_unlock(&usb_lock); - register_usb_transport(usb, serial, 1); + register_usb_transport(usb, serial, NULL, 1); return (1); } diff --git a/adb/usb_linux.c b/adb/usb_linux.c index 4d55b74..7bf2057 100644 --- a/adb/usb_linux.c +++ b/adb/usb_linux.c @@ -116,7 +116,8 @@ static void kick_disconnected_devices() } -static void register_device(const char *dev_name, unsigned char ep_in, unsigned char ep_out, +static void register_device(const char *dev_name, const char *devpath, + unsigned char ep_in, unsigned char ep_out, int ifc, int serial_index, unsigned zero_mask); static inline int badname(const char *name) @@ -129,7 +130,7 @@ static inline int badname(const char *name) static void find_usb_device(const char *base, void (*register_device_callback) - (const char *, unsigned char, unsigned char, int, int, unsigned)) + (const char *, const char *, unsigned char, unsigned char, int, int, unsigned)) { char busname[32], devname[32]; unsigned char local_ep_in, local_ep_out; @@ -227,6 +228,11 @@ static void find_usb_device(const char *base, is_adb_interface(vid, pid, interface->bInterfaceClass, interface->bInterfaceSubClass, interface->bInterfaceProtocol)) { + struct stat st; + char pathbuf[128]; + char link[256]; + char *devpath = NULL; + DBGX("looking for bulk endpoints\n"); // looks like ADB... ep1 = (struct usb_endpoint_descriptor *)bufptr; @@ -263,7 +269,26 @@ static void find_usb_device(const char *base, local_ep_out = ep1->bEndpointAddress; } - register_device_callback(devname, local_ep_in, local_ep_out, + // Determine the device path + if (!fstat(fd, &st) && S_ISCHR(st.st_mode)) { + char *slash; + ssize_t link_len; + snprintf(pathbuf, sizeof(pathbuf), "/sys/dev/char/%d:%d", + major(st.st_rdev), minor(st.st_rdev)); + link_len = readlink(pathbuf, link, sizeof(link) - 1); + if (link_len > 0) { + link[link_len] = '\0'; + slash = strrchr(link, '/'); + if (slash) { + snprintf(pathbuf, sizeof(pathbuf), + "usb:%s", slash + 1); + devpath = pathbuf; + } + } + } + + register_device_callback(devname, devpath, + local_ep_in, local_ep_out, interface->bInterfaceNumber, device->iSerialNumber, zero_mask); break; } @@ -532,7 +557,7 @@ int usb_close(usb_handle *h) return 0; } -static void register_device(const char *dev_name, +static void register_device(const char *dev_name, const char *devpath, unsigned char ep_in, unsigned char ep_out, int interface, int serial_index, unsigned zero_mask) { @@ -644,7 +669,7 @@ static void register_device(const char *dev_name, usb->next->prev = usb; adb_mutex_unlock(&usb_lock); - register_usb_transport(usb, serial, usb->writeable); + register_usb_transport(usb, serial, devpath, usb->writeable); return; fail: diff --git a/adb/usb_linux_client.c b/adb/usb_linux_client.c index 635fa4b..fb1dad0 100644 --- a/adb/usb_linux_client.c +++ b/adb/usb_linux_client.c @@ -19,6 +19,8 @@ #include <unistd.h> #include <string.h> +#include <linux/usb/ch9.h> +#include <linux/usb/functionfs.h> #include <sys/ioctl.h> #include <sys/types.h> #include <dirent.h> @@ -29,20 +31,122 @@ #define TRACE_TAG TRACE_USB #include "adb.h" +#define MAX_PACKET_SIZE_FS 64 +#define MAX_PACKET_SIZE_HS 512 + +#define cpu_to_le16(x) htole16(x) +#define cpu_to_le32(x) htole32(x) struct usb_handle { - int fd; adb_cond_t notify; adb_mutex_t lock; + + int (*write)(usb_handle *h, const void *data, int len); + int (*read)(usb_handle *h, void *data, int len); + void (*kick)(usb_handle *h); + + // Legacy f_adb + int fd; + + // FunctionFS + int control; + int bulk_out; /* "out" from the host's perspective => source for adbd */ + int bulk_in; /* "in" from the host's perspective => sink for adbd */ +}; + +static const struct { + struct usb_functionfs_descs_head header; + struct { + struct usb_interface_descriptor intf; + struct usb_endpoint_descriptor_no_audio source; + struct usb_endpoint_descriptor_no_audio sink; + } __attribute__((packed)) fs_descs, hs_descs; +} __attribute__((packed)) descriptors = { + .header = { + .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC), + .length = cpu_to_le32(sizeof(descriptors)), + .fs_count = 3, + .hs_count = 3, + }, + .fs_descs = { + .intf = { + .bLength = sizeof(descriptors.fs_descs.intf), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bNumEndpoints = 2, + .bInterfaceClass = ADB_CLASS, + .bInterfaceSubClass = ADB_SUBCLASS, + .bInterfaceProtocol = ADB_PROTOCOL, + .iInterface = 1, /* first string from the provided table */ + }, + .source = { + .bLength = sizeof(descriptors.fs_descs.source), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 1 | USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = MAX_PACKET_SIZE_FS, + }, + .sink = { + .bLength = sizeof(descriptors.fs_descs.sink), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 2 | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = MAX_PACKET_SIZE_FS, + }, + }, + .hs_descs = { + .intf = { + .bLength = sizeof(descriptors.hs_descs.intf), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bNumEndpoints = 2, + .bInterfaceClass = ADB_CLASS, + .bInterfaceSubClass = ADB_SUBCLASS, + .bInterfaceProtocol = ADB_PROTOCOL, + .iInterface = 1, /* first string from the provided table */ + }, + .source = { + .bLength = sizeof(descriptors.hs_descs.source), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 1 | USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = MAX_PACKET_SIZE_HS, + }, + .sink = { + .bLength = sizeof(descriptors.hs_descs.sink), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 2 | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = MAX_PACKET_SIZE_HS, + }, + }, +}; + +#define STR_INTERFACE_ "ADB Interface" + +static const struct { + struct usb_functionfs_strings_head header; + struct { + __le16 code; + const char str1[sizeof(STR_INTERFACE_)]; + } __attribute__((packed)) lang0; +} __attribute__((packed)) strings = { + .header = { + .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC), + .length = cpu_to_le32(sizeof(strings)), + .str_count = cpu_to_le32(1), + .lang_count = cpu_to_le32(1), + }, + .lang0 = { + cpu_to_le16(0x0409), /* en-us */ + STR_INTERFACE_, + }, }; -void usb_cleanup() -{ - // nothing to do here -} -static void *usb_open_thread(void *x) + +static void *usb_adb_open_thread(void *x) { struct usb_handle *usb = (struct usb_handle *)x; int fd; @@ -72,14 +176,14 @@ static void *usb_open_thread(void *x) usb->fd = fd; D("[ usb_thread - registering device ]\n"); - register_usb_transport(usb, 0, 1); + register_usb_transport(usb, 0, 0, 1); } // never gets here return 0; } -int usb_write(usb_handle *h, const void *data, int len) +static int usb_adb_write(usb_handle *h, const void *data, int len) { int n; @@ -94,7 +198,7 @@ int usb_write(usb_handle *h, const void *data, int len) return 0; } -int usb_read(usb_handle *h, void *data, int len) +static int usb_adb_read(usb_handle *h, void *data, int len) { int n; @@ -109,14 +213,31 @@ int usb_read(usb_handle *h, void *data, int len) return 0; } -void usb_init() +static void usb_adb_kick(usb_handle *h) +{ + D("usb_kick\n"); + adb_mutex_lock(&h->lock); + adb_close(h->fd); + h->fd = -1; + + // notify usb_adb_open_thread that we are disconnected + adb_cond_signal(&h->notify); + adb_mutex_unlock(&h->lock); +} + +static void usb_adb_init() { usb_handle *h; adb_thread_t tid; int fd; h = calloc(1, sizeof(usb_handle)); + + h->write = usb_adb_write; + h->read = usb_adb_read; + h->kick = usb_adb_kick; h->fd = -1; + adb_cond_init(&h->notify, 0); adb_mutex_init(&h->lock, 0); @@ -133,25 +254,239 @@ void usb_init() } D("[ usb_init - starting thread ]\n"); - if(adb_thread_create(&tid, usb_open_thread, h)){ + if(adb_thread_create(&tid, usb_adb_open_thread, h)){ fatal_errno("cannot create usb thread"); } } -void usb_kick(usb_handle *h) + +static void init_functionfs(struct usb_handle *h) { - D("usb_kick\n"); + ssize_t ret; + + D("OPENING %s\n", USB_FFS_ADB_EP0); + h->control = adb_open(USB_FFS_ADB_EP0, O_RDWR); + if (h->control < 0) { + D("[ %s: cannot open control endpoint: errno=%d]\n", USB_FFS_ADB_EP0, errno); + goto err; + } + + ret = adb_write(h->control, &descriptors, sizeof(descriptors)); + if (ret < 0) { + D("[ %s: write descriptors failed: errno=%d ]\n", USB_FFS_ADB_EP0, errno); + goto err; + } + + ret = adb_write(h->control, &strings, sizeof(strings)); + if (ret < 0) { + D("[ %s: writing strings failed: errno=%d]\n", USB_FFS_ADB_EP0, errno); + goto err; + } + + h->bulk_out = adb_open(USB_FFS_ADB_OUT, O_RDWR); + if (h->bulk_out < 0) { + D("[ %s: cannot open bulk-out ep: errno=%d ]\n", USB_FFS_ADB_OUT, errno); + goto err; + } + + h->bulk_in = adb_open(USB_FFS_ADB_IN, O_RDWR); + if (h->bulk_in < 0) { + D("[ %s: cannot open bulk-in ep: errno=%d ]\n", USB_FFS_ADB_IN, errno); + goto err; + } + + return; + +err: + if (h->bulk_in > 0) { + adb_close(h->bulk_in); + h->bulk_in = -1; + } + if (h->bulk_out > 0) { + adb_close(h->bulk_out); + h->bulk_out = -1; + } + if (h->control > 0) { + adb_close(h->control); + h->control = -1; + } + return; +} + +static void *usb_ffs_open_thread(void *x) +{ + struct usb_handle *usb = (struct usb_handle *)x; + + while (1) { + // wait until the USB device needs opening + adb_mutex_lock(&usb->lock); + while (usb->control != -1) + adb_cond_wait(&usb->notify, &usb->lock); + adb_mutex_unlock(&usb->lock); + + while (1) { + init_functionfs(usb); + + if (usb->control >= 0) + break; + + adb_sleep_ms(1000); + } + + D("[ usb_thread - registering device ]\n"); + register_usb_transport(usb, 0, 0, 1); + } + + // never gets here + return 0; +} + +static int bulk_write(int bulk_in, const char *buf, size_t length) +{ + size_t count = 0; + int ret; + + do { + ret = adb_write(bulk_in, buf + count, length - count); + if (ret < 0) { + if (errno != EINTR) + return ret; + } else { + count += ret; + } + } while (count < length); + + D("[ bulk_write done fd=%d ]\n", bulk_in); + return count; +} + +static int usb_ffs_write(usb_handle *h, const void *data, int len) +{ + int n; + + D("about to write (fd=%d, len=%d)\n", h->bulk_in, len); + n = bulk_write(h->bulk_in, data, len); + if (n != len) { + D("ERROR: fd = %d, n = %d, errno = %d (%s)\n", + h->bulk_in, n, errno, strerror(errno)); + return -1; + } + D("[ done fd=%d ]\n", h->bulk_in); + return 0; +} + +static int bulk_read(int bulk_out, char *buf, size_t length) +{ + size_t count = 0; + int ret; + + do { + ret = adb_read(bulk_out, buf + count, length - count); + if (ret < 0) { + if (errno != EINTR) { + D("[ bulk_read failed fd=%d length=%d count=%d ]\n", + bulk_out, length, count); + return ret; + } + } else { + count += ret; + } + } while (count < length); + + return count; +} + +static int usb_ffs_read(usb_handle *h, void *data, int len) +{ + int n; + + D("about to read (fd=%d, len=%d)\n", h->bulk_out, len); + n = bulk_read(h->bulk_out, data, len); + if (n != len) { + D("ERROR: fd = %d, n = %d, errno = %d (%s)\n", + h->bulk_out, n, errno, strerror(errno)); + return -1; + } + D("[ done fd=%d ]\n", h->bulk_out); + return 0; +} + +static void usb_ffs_kick(usb_handle *h) +{ + int err; + + err = ioctl(h->bulk_in, FUNCTIONFS_CLEAR_HALT); + if (err < 0) + D("[ kick: source (fd=%d) clear halt failed (%d) ]", h->bulk_in, errno); + + err = ioctl(h->bulk_out, FUNCTIONFS_CLEAR_HALT); + if (err < 0) + D("[ kick: sink (fd=%d) clear halt failed (%d) ]", h->bulk_out, errno); + adb_mutex_lock(&h->lock); - adb_close(h->fd); - h->fd = -1; + adb_close(h->control); + adb_close(h->bulk_out); + adb_close(h->bulk_in); + h->control = h->bulk_out = h->bulk_in = -1; - // notify usb_open_thread that we are disconnected + // notify usb_ffs_open_thread that we are disconnected adb_cond_signal(&h->notify); adb_mutex_unlock(&h->lock); } +static void usb_ffs_init() +{ + usb_handle *h; + adb_thread_t tid; + + D("[ usb_init - using FunctionFS ]\n"); + + h = calloc(1, sizeof(usb_handle)); + + h->write = usb_ffs_write; + h->read = usb_ffs_read; + h->kick = usb_ffs_kick; + + h->control = -1; + h->bulk_out = -1; + h->bulk_out = -1; + + adb_cond_init(&h->notify, 0); + adb_mutex_init(&h->lock, 0); + + D("[ usb_init - starting thread ]\n"); + if (adb_thread_create(&tid, usb_ffs_open_thread, h)){ + fatal_errno("[ cannot create usb thread ]\n"); + } +} + +void usb_init() +{ + if (access(USB_FFS_ADB_EP0, F_OK) == 0) + usb_ffs_init(); + else + usb_adb_init(); +} + +void usb_cleanup() +{ +} + +int usb_write(usb_handle *h, const void *data, int len) +{ + return h->write(h, data, len); +} + +int usb_read(usb_handle *h, void *data, int len) +{ + return h->read(h, data, len); +} int usb_close(usb_handle *h) { - // nothing to do here return 0; } + +void usb_kick(usb_handle *h) +{ + h->kick(h); +} diff --git a/adb/usb_osx.c b/adb/usb_osx.c index 00d02da..45ce444 100644 --- a/adb/usb_osx.c +++ b/adb/usb_osx.c @@ -125,10 +125,13 @@ AndroidInterfaceAdded(void *refCon, io_iterator_t iterator) IOUSBDeviceInterface197 **dev = NULL; HRESULT result; SInt32 score; + UInt32 locationId; UInt16 vendor; UInt16 product; UInt8 serialIndex; char serial[256]; + char devpathBuf[64]; + char *devpath = NULL; while ((usbInterface = IOIteratorNext(iterator))) { //* Create an intermediate interface plugin @@ -192,6 +195,11 @@ AndroidInterfaceAdded(void *refCon, io_iterator_t iterator) kr = (*dev)->GetDeviceVendor(dev, &vendor); kr = (*dev)->GetDeviceProduct(dev, &product); + kr = (*dev)->GetLocationID(dev, &locationId); + if (kr == 0) { + snprintf(devpathBuf, sizeof(devpathBuf), "usb:%lX", locationId); + devpath = devpathBuf; + } kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex); if (serialIndex > 0) { @@ -256,7 +264,7 @@ AndroidInterfaceAdded(void *refCon, io_iterator_t iterator) } DBG("AndroidDeviceAdded calling register_usb_transport\n"); - register_usb_transport(handle, (serial[0] ? serial : NULL), 1); + register_usb_transport(handle, (serial[0] ? serial : NULL), devpath, 1); // Register for an interest notification of this device being removed. // Pass the reference to our private data as the refCon for the diff --git a/adb/usb_vendors.c b/adb/usb_vendors.c index b988115..3a8e8fd 100644 --- a/adb/usb_vendors.c +++ b/adb/usb_vendors.c @@ -127,6 +127,11 @@ #define VENDOR_ID_LAB126 0x1949 // Yulong Coolpad's USB Vendor ID #define VENDOR_ID_YULONG_COOLPAD 0x1EBF +// Kobo's USB Vendor ID +#define VENDOR_ID_KOBO 0x2237 +// Teleepoch's USB Vendor ID +#define VENDOR_ID_TELEEPOCH 0x2340 + /** built-in vendor list */ int builtInVendorIds[] = { @@ -176,6 +181,8 @@ int builtInVendorIds[] = { VENDOR_ID_SONY, VENDOR_ID_LAB126, VENDOR_ID_YULONG_COOLPAD, + VENDOR_ID_KOBO, + VENDOR_ID_TELEEPOCH, }; #define BUILT_IN_VENDOR_COUNT (sizeof(builtInVendorIds)/sizeof(builtInVendorIds[0])) @@ -228,6 +235,7 @@ void usb_vendors_init(void) break; } } + fclose(f); } } } diff --git a/adb/usb_windows.c b/adb/usb_windows.c index 251bf17..4936b77 100644 --- a/adb/usb_windows.c +++ b/adb/usb_windows.c @@ -490,7 +490,7 @@ void find_devices() { true)) { // Lets make sure that we don't duplicate this device if (register_new_device(handle)) { - register_usb_transport(handle, serial_number, 1); + register_usb_transport(handle, serial_number, NULL, 1); } else { D("register_new_device failed for %s\n", interf_name); usb_cleanup_handle(handle); diff --git a/charger/Android.mk b/charger/Android.mk index 5367a98..0258604 100644 --- a/charger/Android.mk +++ b/charger/Android.mk @@ -8,6 +8,14 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ charger.c +ifeq ($(strip $(BOARD_CHARGER_DISABLE_INIT_BLANK)),true) +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 @@ -17,7 +25,10 @@ LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED) LOCAL_C_INCLUDES := bootable/recovery LOCAL_STATIC_LIBRARIES := libminui libpixelflinger_static libpng -LOCAL_STATIC_LIBRARIES += libz libstdc++ libcutils libc +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 abf5517..353bdf0 100644 --- a/charger/charger.c +++ b/charger/charger.c @@ -21,25 +21,30 @@ #include <errno.h> #include <fcntl.h> #include <linux/input.h> -#include <linux/netlink.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/poll.h> -#include <sys/socket.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/un.h> #include <time.h> #include <unistd.h> +#include <sys/socket.h> +#include <linux/netlink.h> + #include <cutils/android_reboot.h> #include <cutils/klog.h> #include <cutils/list.h> #include <cutils/misc.h> #include <cutils/uevent.h> +#ifdef CHARGER_ENABLE_SUSPEND +#include <suspend/autosuspend.h> +#endif + #include "minui/minui.h" #ifndef max @@ -351,6 +356,21 @@ static void remove_supply(struct charger *charger, struct power_supply *supply) 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 = ""; @@ -684,6 +704,8 @@ static void update_screen_state(struct charger *charger, int64_t now) 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; } @@ -823,8 +845,10 @@ static void process_key(struct charger *charger, int code, int64_t now) } } 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); + } } } @@ -842,6 +866,7 @@ static void handle_input_state(struct charger *charger, int64_t now) 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", @@ -974,7 +999,9 @@ int main(int argc, char **argv) ev_sync_key_state(set_key_callback, charger); +#ifndef CHARGER_DISABLE_INIT_BLANK gr_fb_blank(true); +#endif charger->next_screen_transition = now - 1; charger->next_key_check = -1; 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 int total_size = 0; 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 @@ static void fix_stat(const char *path, struct stat *s) } 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 15083f4..3fca64f 100644 --- a/debuggerd/Android.mk +++ b/debuggerd/Android.mk @@ -23,13 +23,11 @@ ifeq ($(ARCH_ARM_HAVE_VFP_D32),true) LOCAL_CFLAGS += -DWITH_VFP_D32 endif # ARCH_ARM_HAVE_VFP_D32 -LOCAL_SHARED_LIBRARIES := libcutils libc libcorkscrew - -ifeq ($(HAVE_SELINUX),true) -LOCAL_SHARED_LIBRARIES += libselinux -LOCAL_C_INCLUDES += external/libselinux/include -LOCAL_CFLAGS += -DHAVE_SELINUX -endif +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libc \ + libcorkscrew \ + libselinux include $(BUILD_EXECUTABLE) @@ -39,6 +37,7 @@ LOCAL_SRC_FILES += $(TARGET_ARCH)/crashglue.S 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/arm/machine.c b/debuggerd/arm/machine.c index 1c2e13f..160db7b 100644 --- a/debuggerd/arm/machine.c +++ b/debuggerd/arm/machine.c @@ -53,7 +53,8 @@ static void dump_memory(log_t* log, pid_t tid, uintptr_t addr, bool at_fault) { /* catch underflow */ p = 0; } - end = p + 80; + /* Dump more memory content for the crashing thread. */ + end = p + 256; /* catch overflow; 'end - p' has to be multiples of 16 */ while (end < p) end -= 16; @@ -81,6 +82,8 @@ static void dump_memory(log_t* log, pid_t tid, uintptr_t addr, bool at_fault) { long data = ptrace(PTRACE_PEEKTEXT, tid, (void*)p, NULL); sprintf(code_buffer + strlen(code_buffer), "%08lx ", data); + /* Enable the following code blob to dump ASCII values */ +#if 0 int j; for (j = 0; j < 4; j++) { /* @@ -95,6 +98,7 @@ static void dump_memory(log_t* log, pid_t tid, uintptr_t addr, bool at_fault) { *asc_out++ = '.'; } } +#endif p += 4; } *asc_out = '\0'; diff --git a/debuggerd/backtrace.c b/debuggerd/backtrace.c index 62f7f32..ba76e7d 100644 --- a/debuggerd/backtrace.c +++ b/debuggerd/backtrace.c @@ -125,10 +125,9 @@ void dump_backtrace(int fd, pid_t pid, pid_t tid, bool* detach_failed, char task_path[64]; snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid); DIR* d = opendir(task_path); - if (d) { - struct dirent debuf; - struct dirent *de; - while (!readdir_r(d, &debuf, &de) && de) { + if (d != NULL) { + struct dirent* de = NULL; + while ((de = readdir(d)) != NULL) { if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) { continue; } diff --git a/debuggerd/crasher.c b/debuggerd/crasher.c index 00652e9..630d980 100644 --- a/debuggerd/crasher.c +++ b/debuggerd/crasher.c @@ -20,6 +20,7 @@ void crash1(void); void crashnostack(void); void maybeabort(void); +int do_action(const char* arg); static void debuggerd_connect() { @@ -34,6 +35,18 @@ static void debuggerd_connect() } } +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]); +} + void test_call1() { *((int*) 32) = 1; @@ -74,24 +87,63 @@ int ctest() return 0; } -int main(int argc, char **argv) +static void* thread_callback(void* raw_arg) +{ + return (void*) do_action((const char*) raw_arg); +} + +int do_action_on_thread(const char* arg) { + pthread_t t; + pthread_create(&t, NULL, thread_callback, (void*) arg); + void* result = NULL; + pthread_join(t, &result); + return (int) result; +} + +__attribute__((noinline)) int crash3(int a) { + *((int*) 0xdead) = a; + return a*4; +} + +__attribute__((noinline)) int crash2(int a) { + a = crash3(a) + 2; + return a*3; +} + +__attribute__((noinline)) int crash(int a) { + a = crash2(a) + 1; + return a*2; +} + +int do_action(const char* arg) +{ + if(!strncmp(arg, "thread-", strlen("thread-"))) { + return do_action_on_thread(arg + strlen("thread-")); + } + + if(!strcmp(arg,"smash-stack")) return smash_stack(42); + if(!strcmp(arg,"nostack")) crashnostack(); + if(!strcmp(arg,"ctest")) return ctest(); + if(!strcmp(arg,"exit")) exit(1); + if(!strcmp(arg,"crash")) return crash(42); + 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,"crasher: " __TIME__ "!@\n"); +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) { - if(!strcmp(argv[1],"nostack")) crashnostack(); - if(!strcmp(argv[1],"ctest")) return ctest(); - if(!strcmp(argv[1],"exit")) exit(1); - if(!strcmp(argv[1],"abort")) maybeabort(); - - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - pthread_create(&thr, &attr, test_thread, 0); - while(1) sleep(1); + return do_action(argv[1]); } else { crash1(); // *((int*) 0) = 42; diff --git a/debuggerd/tombstone.c b/debuggerd/tombstone.c index 012337b..e8b3e24 100644 --- a/debuggerd/tombstone.c +++ b/debuggerd/tombstone.c @@ -35,9 +35,7 @@ #include <corkscrew/demangle.h> #include <corkscrew/backtrace.h> -#ifdef HAVE_SELINUX #include <selinux/android.h> -#endif #include "machine.h" #include "tombstone.h" @@ -86,6 +84,7 @@ static const char *get_signame(int sig) static const char *get_sigcode(int signo, int code) { + // Try the signal-specific codes... switch (signo) { case SIGILL: switch (code) { @@ -124,10 +123,43 @@ static const char *get_sigcode(int signo, int code) case SEGV_ACCERR: return "SEGV_ACCERR"; } break; + case SIGTRAP: + switch (code) { + case TRAP_BRKPT: return "TRAP_BRKPT"; + case TRAP_TRACE: return "TRAP_TRACE"; + } + break; + } + // Then the other codes... + switch (code) { + case SI_USER: return "SI_USER"; +#if defined(SI_KERNEL) + case SI_KERNEL: return "SI_KERNEL"; +#endif + case SI_QUEUE: return "SI_QUEUE"; + case SI_TIMER: return "SI_TIMER"; + case SI_MESGQ: return "SI_MESGQ"; + case SI_ASYNCIO: return "SI_ASYNCIO"; +#if defined(SI_SIGIO) + case SI_SIGIO: return "SI_SIGIO"; +#endif +#if defined(SI_TKILL) + case SI_TKILL: return "SI_TKILL"; +#endif } + // Then give up... return "?"; } +static void dump_revision_info(log_t* log) +{ + char revision[PROPERTY_VALUE_MAX]; + + property_get("ro.revision", revision, "unknown"); + + _LOG(log, false, "Revision: '%s'\n", revision); +} + static void dump_build_info(log_t* log) { char fingerprint[PROPERTY_VALUE_MAX]; @@ -318,6 +350,18 @@ static void dump_backtrace_and_stack(const ptrace_context_t* context, log_t* log } } +static void dump_map(log_t* log, map_info_t* m, const char* what) { + if (m != NULL) { + _LOG(log, false, " %08x-%08x %c%c%c %s\n", m->start, m->end, + m->is_readable ? 'r' : '-', + m->is_writable ? 'w' : '-', + m->is_executable ? 'x' : '-', + m->name); + } else { + _LOG(log, false, " (no %s)\n", what); + } +} + static void dump_nearby_maps(const ptrace_context_t* context, log_t* log, pid_t tid) { siginfo_t si; memset(&si, 0, sizeof(si)); @@ -364,21 +408,9 @@ static void dump_nearby_maps(const ptrace_context_t* context, log_t* log, pid_t * Show "next" then "match" then "prev" so that the addresses appear in * ascending order (like /proc/pid/maps). */ - if (next != NULL) { - _LOG(log, false, " %08x-%08x %s\n", next->start, next->end, next->name); - } else { - _LOG(log, false, " (no map below)\n"); - } - if (map != NULL) { - _LOG(log, false, " %08x-%08x %s\n", map->start, map->end, map->name); - } else { - _LOG(log, false, " (no map for address)\n"); - } - if (prev != NULL) { - _LOG(log, false, " %08x-%08x %s\n", prev->start, prev->end, prev->name); - } else { - _LOG(log, false, " (no map above)\n"); - } + dump_map(log, next, "map below"); + dump_map(log, map, "map for address"); + dump_map(log, prev, "map above"); } static void dump_thread(const ptrace_context_t* context, log_t* log, pid_t tid, bool at_fault, @@ -407,9 +439,8 @@ static bool dump_sibling_thread_report(const ptrace_context_t* context, } bool detach_failed = false; - struct dirent debuf; - struct dirent *de; - while (!readdir_r(d, &debuf, &de) && de) { + struct dirent* de; + while ((de = readdir(d)) != NULL) { /* Ignore "." and ".." */ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) { continue; @@ -599,6 +630,7 @@ static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, _LOG(log, false, "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n"); dump_build_info(log); + dump_revision_info(log); dump_thread_info(log, pid, tid, true); if(signal) { dump_fault_addr(log, tid, signal); @@ -686,12 +718,10 @@ char* engrave_tombstone(pid_t pid, pid_t tid, int signal, mkdir(TOMBSTONE_DIR, 0755); chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM); -#ifdef HAVE_SELINUX if (selinux_android_restorecon(TOMBSTONE_DIR) == -1) { *detach_failed = false; return NULL; } -#endif int fd; char* path = find_and_open_tombstone(&fd); diff --git a/fastboot/Android.mk b/fastboot/Android.mk index 905f759..5025dae 100644 --- a/fastboot/Android.mk +++ b/fastboot/Android.mk @@ -57,15 +57,13 @@ LOCAL_STATIC_LIBRARIES := \ libz ifneq ($(HOST_OS),windows) -ifeq ($(HAVE_SELINUX), true) LOCAL_STATIC_LIBRARIES += libselinux -endif # HAVE_SELINUX endif # HOST_OS != windows include $(BUILD_HOST_EXECUTABLE) -$(call dist-for-goals,dist_files,$(LOCAL_BUILT_MODULE)) +$(call dist-for-goals,dist_files sdk,$(LOCAL_BUILT_MODULE)) ifeq ($(HOST_OS),linux) diff --git a/fastboot/engine.c b/fastboot/engine.c index 93be3de..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() @@ -144,6 +143,39 @@ struct generator { { "ext4", generate_ext4_image, cleanup_image } }; +/* Return true if this partition is supported by the fastboot format command. + * It is also used to determine if we should first erase a partition before + * flashing it with an ext4 filesystem. See needs_erase() + * + * Not all devices report the filesystem type, so don't report any errors, + * just return false. + */ +int fb_format_supported(usb_handle *usb, const char *partition) +{ + char response[FB_RESPONSE_SZ+1]; + struct generator *generator = NULL; + int status; + unsigned int i; + + status = fb_getvar(usb, response, "partition-type:%s", partition); + if (status) { + return 0; + } + + for (i = 0; i < ARRAY_SIZE(generators); i++) { + if (!strncmp(generators[i].fs_type, response, FB_RESPONSE_SZ)) { + generator = &generators[i]; + break; + } + } + + if (generator) { + return 1; + } + + return 0; +} + static int cb_default(Action *a, int status, char *resp) { if (status) { @@ -269,10 +301,7 @@ void generate_ext4_image(struct image_data *image) #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; @@ -565,6 +594,8 @@ int fb_execute_queue(usb_handle *usb) int status = 0; a = action_list; + if (!a) + return status; resp[FB_RESPONSE_SZ] = 0; double start = -1; @@ -605,3 +636,8 @@ int fb_execute_queue(usb_handle *usb) fprintf(stderr,"finished. total time: %.3fs\n", (now() - start)); return status; } + +int fb_queue_is_empty(void) +{ + return (action_list == NULL); +} diff --git a/fastboot/fastboot.c b/fastboot/fastboot.c index b6eab8c..3de6d7d 100644 --- a/fastboot/fastboot.c +++ b/fastboot/fastboot.c @@ -70,6 +70,7 @@ static const char *product = 0; static const char *cmdline = 0; static int wipe_data = 0; static unsigned short vendor_id = 0; +static int long_listing = 0; static int64_t sparse_limit = -1; static int64_t target_sparse_limit = -1; @@ -187,6 +188,11 @@ oops: int match_fastboot(usb_ifc_info *info) { + return match_fastboot_with_serial(info, serial); +} + +int match_fastboot_with_serial(usb_ifc_info *info, const char *local_serial) +{ if(!(vendor_id && (info->dev_vendor == vendor_id)) && (info->dev_vendor != 0x18d1) && // Google (info->dev_vendor != 0x8087) && // Intel @@ -204,15 +210,16 @@ int match_fastboot(usb_ifc_info *info) if(info->ifc_class != 0xff) return -1; if(info->ifc_subclass != 0x42) return -1; if(info->ifc_protocol != 0x03) return -1; - // require matching serial number if a serial number is specified + // require matching serial number or device path if requested // at the command line with the -s option. - if (serial && strcmp(serial, info->serial_number) != 0) return -1; + if (local_serial && (strcmp(local_serial, info->serial_number) != 0 && + strcmp(local_serial, info->device_path) != 0)) return -1; return 0; } int list_devices_callback(usb_ifc_info *info) { - if (match_fastboot(info) == 0) { + if (match_fastboot_with_serial(info, NULL) == 0) { char* serial = info->serial_number; if (!info->writable) { serial = "no permissions"; // like "adb devices" @@ -221,7 +228,13 @@ int list_devices_callback(usb_ifc_info *info) serial = "????????????"; } // output compatible with "adb devices" - printf("%s\tfastboot\n", serial); + if (!long_listing) { + printf("%s\tfastboot\n", serial); + } else if (!info->device_path) { + printf("%-22s fastboot\n", serial); + } else { + printf("%-22s fastboot %s\n", serial, info->device_path); + } } return -1; @@ -274,8 +287,13 @@ void usage(void) " help show this help message\n" "\n" "options:\n" - " -w erase userdata and cache\n" - " -s <serial number> specify device serial number\n" + " -w erase userdata and cache (and format\n" + " if supported by partition type)\n" + " -u do not first erase partition before\n" + " formatting\n" + " -s <specific device> specify device serial number\n" + " or path to device port\n" + " -l with \"devices\", lists device paths\n" " -p <product> specify product name\n" " -c <cmdline> override kernel commandline\n" " -i <vendor id> specify a custom USB vendor id\n" @@ -547,6 +565,18 @@ static int64_t get_sparse_limit(struct usb_handle *usb, int64_t size) return 0; } +/* Until we get lazy inode table init working in make_ext4fs, we need to + * erase partitions of type ext4 before flashing a filesystem so no stale + * inodes are left lying around. Otherwise, e2fsck gets very upset. + */ +static int needs_erase(const char *part) +{ + /* The function fb_format_supported() currently returns the value + * we want, so just call it. + */ + return fb_format_supported(usb, part); +} + void do_flash(usb_handle *usb, const char *pname, const char *fname) { int64_t sz64; @@ -582,7 +612,7 @@ void do_update_signature(zipfile_t zip, char *fn) fb_queue_command("signature", "installing signature"); } -void do_update(char *fn) +void do_update(char *fn, int erase_first) { void *zdata; unsigned zsize; @@ -620,17 +650,26 @@ void do_update(char *fn) data = unzip_file(zip, "boot.img", &sz); if (data == 0) die("update package missing boot.img"); do_update_signature(zip, "boot.sig"); + if (erase_first && needs_erase("boot")) { + fb_queue_erase("boot"); + } fb_queue_flash("boot", data, sz); data = unzip_file(zip, "recovery.img", &sz); if (data != 0) { do_update_signature(zip, "recovery.sig"); + if (erase_first && needs_erase("recovery")) { + fb_queue_erase("recovery"); + } fb_queue_flash("recovery", data, sz); } data = unzip_file(zip, "system.img", &sz); if (data == 0) die("update package missing system.img"); do_update_signature(zip, "system.sig"); + if (erase_first && needs_erase("system")) { + fb_queue_erase("system"); + } fb_queue_flash("system", data, sz); } @@ -652,7 +691,7 @@ void do_send_signature(char *fn) fb_queue_command("signature", "installing signature"); } -void do_flashall(void) +void do_flashall(int erase_first) { char *fname; void *data; @@ -672,12 +711,18 @@ void do_flashall(void) data = load_file(fname, &sz); if (data == 0) die("could not load boot.img: %s", strerror(errno)); do_send_signature(fname); + if (erase_first && needs_erase("boot")) { + fb_queue_erase("boot"); + } fb_queue_flash("boot", data, sz); fname = find_item("recovery", product); data = load_file(fname, &sz); if (data != 0) { do_send_signature(fname); + if (erase_first && needs_erase("recovery")) { + fb_queue_erase("recovery"); + } fb_queue_flash("recovery", data, sz); } @@ -685,6 +730,9 @@ void do_flashall(void) data = load_file(fname, &sz); if (data == 0) die("could not load system.img: %s", strerror(errno)); do_send_signature(fname); + if (erase_first && needs_erase("system")) { + fb_queue_erase("system"); + } fb_queue_flash("system", data, sz); } @@ -755,6 +803,7 @@ int main(int argc, char **argv) int wants_wipe = 0; int wants_reboot = 0; int wants_reboot_bootloader = 0; + int erase_first = 1; void *data; unsigned sz; unsigned page_size = 2048; @@ -767,7 +816,7 @@ int main(int argc, char **argv) serial = getenv("ANDROID_SERIAL"); while (1) { - c = getopt_long(argc, argv, "wb:n:s:S:p:c:i:m:h", &longopts, NULL); + c = getopt_long(argc, argv, "wub:n:s:S:lp:c:i:m:h", &longopts, NULL); if (c < 0) { break; } @@ -776,6 +825,9 @@ int main(int argc, char **argv) case 'w': wants_wipe = 1; break; + case 'u': + erase_first = 0; + break; case 'b': base_addr = strtoul(optarg, 0, 16); break; @@ -792,6 +844,9 @@ int main(int argc, char **argv) die("invalid sparse limit"); } break; + case 'l': + long_listing = 1; + break; case 'p': product = optarg; break; @@ -832,6 +887,11 @@ int main(int argc, char **argv) return 0; } + if (argc > 0 && !strcmp(*argv, "help")) { + usage(); + return 0; + } + usb = open_device(); while (argc > 0) { @@ -841,10 +901,18 @@ int main(int argc, char **argv) skip(2); } else if(!strcmp(*argv, "erase")) { require(2); + + if (fb_format_supported(usb, argv[1])) { + fprintf(stderr, "******** Did you mean to fastboot format this partition?\n"); + } + fb_queue_erase(argv[1]); skip(2); } else if(!strcmp(*argv, "format")) { require(2); + if (erase_first && needs_erase(argv[1])) { + fb_queue_erase(argv[1]); + } fb_queue_format(argv[1], 0); skip(2); } else if(!strcmp(*argv, "signature")) { @@ -892,6 +960,9 @@ int main(int argc, char **argv) skip(2); } if (fname == 0) die("cannot determine image filename for '%s'", pname); + if (erase_first && needs_erase(pname)) { + fb_queue_erase(pname); + } do_flash(usb, pname, fname); } else if(!strcmp(*argv, "flash:raw")) { char *pname = argv[1]; @@ -909,22 +980,19 @@ int main(int argc, char **argv) fb_queue_flash(pname, data, sz); } else if(!strcmp(*argv, "flashall")) { skip(1); - do_flashall(); + do_flashall(erase_first); wants_reboot = 1; } else if(!strcmp(*argv, "update")) { if (argc > 1) { - do_update(argv[1]); + do_update(argv[1], erase_first); skip(2); } else { - do_update("update.zip"); + do_update("update.zip", erase_first); skip(1); } wants_reboot = 1; } else if(!strcmp(*argv, "oem")) { argc = do_oem_command(argc, argv); - } else if (!strcmp(*argv, "help")) { - usage(); - return 0; } else { usage(); return 1; @@ -943,6 +1011,9 @@ int main(int argc, char **argv) fb_queue_command("reboot-bootloader", "rebooting into bootloader"); } + if (fb_queue_is_empty()) + return 0; + status = fb_execute_queue(usb); return (status) ? 1 : 0; } diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h index 9177932..c1b2964 100644 --- a/fastboot/fastboot.h +++ b/fastboot/fastboot.h @@ -45,7 +45,8 @@ char *fb_get_error(void); /* engine.c - high level command queue engine */ int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...); -void fb_queue_flash(const char *ptn, void *data, unsigned sz);; +int fb_format_supported(usb_handle *usb, const char *partition); +void fb_queue_flash(const char *ptn, void *data, unsigned sz); void fb_queue_flash_sparse(const char *ptn, struct sparse_file *s, unsigned sz); void fb_queue_erase(const char *ptn); void fb_queue_format(const char *ptn, int skip_if_not_supported); @@ -58,6 +59,7 @@ void fb_queue_command(const char *cmd, const char *msg); void fb_queue_download(const char *name, void *data, unsigned size); void fb_queue_notice(const char *notice); int fb_execute_queue(usb_handle *usb); +int fb_queue_is_empty(void); /* util stuff */ void die(const char *fmt, ...); diff --git a/fastboot/usb.h b/fastboot/usb.h index df9efde..d504ee2 100644 --- a/fastboot/usb.h +++ b/fastboot/usb.h @@ -53,6 +53,7 @@ struct usb_ifc_info unsigned char writable; char serial_number[256]; + char device_path[256]; }; typedef int (*ifc_match_func)(usb_ifc_info *ifc); diff --git a/fastboot/usb_linux.c b/fastboot/usb_linux.c index 83c6de9..b7a9ca3 100644 --- a/fastboot/usb_linux.c +++ b/fastboot/usb_linux.c @@ -32,6 +32,7 @@ #include <string.h> #include <sys/ioctl.h> +#include <sys/stat.h> #include <sys/types.h> #include <dirent.h> #include <fcntl.h> @@ -107,6 +108,9 @@ static int filter_usb_device(int fd, char *ptr, int len, int writable, int in, out; unsigned i; unsigned e; + + struct stat st; + int result; if(check(ptr, len, USB_DT_DEVICE, USB_DT_DEVICE_SIZE)) return -1; @@ -134,7 +138,6 @@ static int filter_usb_device(int fd, char *ptr, int len, int writable, // Keep it short enough because some bootloaders are borked if the URB len is > 255 // 128 is too big by 1. __u16 buffer[127]; - int result; memset(buffer, 0, sizeof(buffer)); @@ -158,6 +161,42 @@ static int filter_usb_device(int fd, char *ptr, int len, int writable, } } + /* We need to get a path that represents a particular port on a particular + * hub. We are passed an fd that was obtained by opening an entry under + * /dev/bus/usb. Unfortunately, the names of those entries change each + * time devices are plugged and unplugged. So how to get a repeatable + * path? udevadm provided the inspiration. We can get the major and + * minor of the device file, read the symlink that can be found here: + * /sys/dev/char/<major>:<minor> + * and then use the last element of that path. As a concrete example, I + * have an Android device at /dev/bus/usb/001/027 so working with bash: + * $ ls -l /dev/bus/usb/001/027 + * crw-rw-r-- 1 root plugdev 189, 26 Apr 9 11:03 /dev/bus/usb/001/027 + * $ ls -l /sys/dev/char/189:26 + * lrwxrwxrwx 1 root root 0 Apr 9 11:03 /sys/dev/char/189:26 -> + * ../../devices/pci0000:00/0000:00:1a.7/usb1/1-4/1-4.2/1-4.2.3 + * So our device_path would be 1-4.2.3 which says my device is connected + * to port 3 of a hub on port 2 of a hub on port 4 of bus 1 (per + * http://www.linux-usb.org/FAQ.html). + */ + info.device_path[0] = '\0'; + result = fstat(fd, &st); + if (!result && S_ISCHR(st.st_mode)) { + char cdev[128]; + char link[256]; + char *slash; + ssize_t link_len; + snprintf(cdev, sizeof(cdev), "/sys/dev/char/%d:%d", + major(st.st_rdev), minor(st.st_rdev)); + link_len = readlink(cdev, link, sizeof(link) - 1); + if (link_len > 0) { + link[link_len] = '\0'; + slash = strrchr(link, '/'); + if (slash) + snprintf(info.device_path, sizeof(info.device_path), "usb:%s", slash+1); + } + } + for(i = 0; i < cfg->bNumInterfaces; i++) { if(check(ptr, len, USB_DT_INTERFACE, USB_DT_INTERFACE_SIZE)) return -1; diff --git a/fastboot/usb_osx.c b/fastboot/usb_osx.c index cbce9bd..1548ba8 100644 --- a/fastboot/usb_osx.c +++ b/fastboot/usb_osx.c @@ -264,6 +264,7 @@ static int try_device(io_service_t device, usb_handle *handle) { SInt32 score; HRESULT result; UInt8 serialIndex; + UInt32 locationId; // Create an intermediate plugin. kr = IOCreatePlugInInterfaceForService(device, @@ -322,6 +323,13 @@ static int try_device(io_service_t device, usb_handle *handle) { goto error; } + kr = (*dev)->GetLocationID(dev, &locationId); + if (kr != 0) { + ERR("GetLocationId"); + goto error; + } + snprintf(handle->info.device_path, sizeof(handle->info.device_path), "usb:%lX", locationId); + kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex); if (serialIndex > 0) { diff --git a/fastboot/usb_windows.c b/fastboot/usb_windows.c index 99027cc..7aa36b2 100644 --- a/fastboot/usb_windows.c +++ b/fastboot/usb_windows.c @@ -311,6 +311,8 @@ int recognized_device(usb_handle* handle, ifc_match_func callback) { info.serial_number[0] = 0; } + info.device_path[0] = 0; + if (callback(&info) == 0) { return 1; } diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c index 0361ab8..e51c9cf 100644 --- a/fs_mgr/fs_mgr.c +++ b/fs_mgr/fs_mgr.c @@ -58,6 +58,12 @@ static struct flag_list mount_flags[] = { { "ro", MS_RDONLY }, { "rw", 0 }, { "remount", MS_REMOUNT }, + { "bind", MS_BIND }, + { "rec", MS_REC }, + { "unbindable", MS_UNBINDABLE }, + { "private", MS_PRIVATE }, + { "slave", MS_SLAVE }, + { "shared", MS_SHARED }, { "defaults", 0 }, { 0, 0 }, }; @@ -167,7 +173,7 @@ out: * then return an empty buffer. This effectively ignores lines that are too long. * On EOF, return null. */ -static char *getline(char *buf, int size, FILE *file) +static char *fs_getline(char *buf, int size, FILE *file) { int cnt = 0; int eof = 0; @@ -241,7 +247,7 @@ static struct fstab_rec *read_fstab(char *fstab_path) } entries = 0; - while (getline(line, sizeof(line), fstab_file)) { + while (fs_getline(line, sizeof(line), fstab_file)) { /* if the last character is a newline, shorten the string by 1 byte */ len = strlen(line); if (line[len - 1] == '\n') { @@ -268,7 +274,7 @@ static struct fstab_rec *read_fstab(char *fstab_path) fseek(fstab_file, 0, SEEK_SET); cnt = 0; - while (getline(line, sizeof(line), fstab_file)) { + while (fs_getline(line, sizeof(line), fstab_file)) { /* if the last character is a newline, shorten the string by 1 byte */ len = strlen(line); if (line[len - 1] == '\n') { @@ -358,13 +364,34 @@ static void free_fstab(struct fstab_rec *fstab) free(fstab); } -static void check_fs(char *blk_dev, char *type) +static void check_fs(char *blk_dev, char *type, char *target) { pid_t pid; int status; + int ret; + long tmpmnt_flags = MS_NOATIME | MS_NOEXEC | MS_NOSUID; + char *tmpmnt_opts = "nomblk_io_submit,errors=remount-ro"; /* Check for the types of filesystems we know how to check */ if (!strcmp(type, "ext2") || !strcmp(type, "ext3") || !strcmp(type, "ext4")) { + /* + * First try to mount and unmount the filesystem. We do this because + * the kernel is more efficient than e2fsck in running the journal and + * processing orphaned inodes, and on at least one device with a + * performance issue in the emmc firmware, it can take e2fsck 2.5 minutes + * to do what the kernel does in about a second. + * + * After mounting and unmounting the filesystem, run e2fsck, and if an + * error is recorded in the filesystem superblock, e2fsck will do a full + * check. Otherwise, it does nothing. If the kernel cannot mount the + * filesytsem due to an error, e2fsck is still run to do a full check + * fix the filesystem. + */ + ret = mount(blk_dev, target, type, tmpmnt_flags, tmpmnt_opts); + if (! ret) { + umount(target); + } + INFO("Running %s on %s\n", E2FSCK_BIN, blk_dev); pid = fork(); if (pid > 0) { @@ -434,7 +461,7 @@ int fs_mgr_mount_all(char *fstab_file) } if (fstab[i].fs_mgr_flags & MF_CHECK) { - check_fs(fstab[i].blk_dev, fstab[i].type); + check_fs(fstab[i].blk_dev, fstab[i].type, fstab[i].mnt_point); } mret = mount(fstab[i].blk_dev, fstab[i].mnt_point, fstab[i].type, @@ -496,11 +523,11 @@ int fs_mgr_do_mount(char *fstab_file, char *n_name, char *n_blk_dev, char *tmp_m /* 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); + check_fs(n_blk_dev, fstab[i].type, fstab[i].mnt_point); } /* Now mount it where requested */ diff --git a/include/arch/darwin-x86/AndroidConfig.h b/include/arch/darwin-x86/AndroidConfig.h deleted file mode 100644 index 48f8d9a..0000000 --- a/include/arch/darwin-x86/AndroidConfig.h +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -/* - * Android config -- "Darwin". Used for X86 Mac OS X. - */ -#ifndef _ANDROID_CONFIG_H -#define _ANDROID_CONFIG_H - -/* - * =========================================================================== - * !!! IMPORTANT !!! - * =========================================================================== - * - * This file is included by ALL C/C++ source files. Don't put anything in - * here unless you are absolutely certain it can't go anywhere else. - * - * Any C++ stuff must be wrapped with "#ifdef __cplusplus". Do not use "//" - * comments. - */ - -/* - * Threading model. Choose one: - * - * HAVE_PTHREADS - use the pthreads library. - * HAVE_WIN32_THREADS - use Win32 thread primitives. - * -- combine HAVE_CREATETHREAD, HAVE_CREATEMUTEX, and HAVE__BEGINTHREADEX - */ -#define HAVE_PTHREADS - -/* - * Do we have the futex syscall? - */ - -/* #define HAVE_FUTEX */ - -/* - * Process creation model. Choose one: - * - * HAVE_FORKEXEC - use fork() and exec() - * HAVE_WIN32_PROC - use CreateProcess() - */ -#define HAVE_FORKEXEC - -/* - * Process out-of-memory adjustment. Set if running on Linux, - * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory - * badness adjustment. - */ -/* #define HAVE_OOM_ADJ */ - -/* - * IPC model. Choose one: - * - * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget). - * HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap). - * HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping). - * HAVE_ANDROID_IPC - use Android versions (?, mmap). - */ -#define HAVE_MACOSX_IPC - -/* - * Memory-mapping model. Choose one: - * - * HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h - * HAVE_WIN32_FILEMAP - use Win32 filemaps - */ -#define HAVE_POSIX_FILEMAP - -/* - * Define this if you have <termio.h> - */ -#define HAVE_TERMIO_H - -/* - * Define this if you have <sys/sendfile.h> - */ -/* #define HAVE_SYS_SENDFILE_H 1 */ - -/* - * Define this if you build against MSVCRT.DLL - */ -/* #define HAVE_MS_C_RUNTIME */ - -/* - * Define this if you have sys/uio.h - */ -#define HAVE_SYS_UIO_H - -/* - * Define this if your platforms implements symbolic links - * in its filesystems - */ -#define HAVE_SYMLINKS - -/* - * Define this if we have localtime_r(). - */ -#define HAVE_LOCALTIME_R 1 - -/* - * Define this if we have gethostbyname_r(). - */ -/* #define HAVE_GETHOSTBYNAME_R */ - -/* - * Define this if we have ioctl(). - */ -/* #define HAVE_IOCTL */ - -/* - * Define this if we want to use WinSock. - */ -/* #define HAVE_WINSOCK */ - -/* - * Define this if have clock_gettime() and friends - */ -/* #define HAVE_POSIX_CLOCKS */ - -/* - * Define this if we have pthread_cond_timedwait_monotonic() and - * clock_gettime(CLOCK_MONOTONIC). - */ -/* #define HAVE_TIMEDWAIT_MONOTONIC */ - -/* - * Endianness of the target machine. Choose one: - * - * HAVE_ENDIAN_H -- have endian.h header we can include. - * HAVE_LITTLE_ENDIAN -- we are little endian. - * HAVE_BIG_ENDIAN -- we are big endian. - */ -#if (defined(__ppc__) || defined(__ppc64__)) -# define HAVE_BIG_ENDIAN -#elif (defined(__i386__) || defined(__x86_64__)) -# define HAVE_LITTLE_ENDIAN -#endif - -/* - * We need to choose between 32-bit and 64-bit off_t. All of our code should - * agree on the same size. For desktop systems, use 64-bit values, - * because some of our libraries (e.g. wxWidgets) expect to be built that way. - */ -#define _FILE_OFFSET_BITS 64 -#define _LARGEFILE_SOURCE 1 - -/* - * Define if platform has off64_t (and lseek64 and other xxx64 functions) - */ -/* #define HAVE_OFF64_T */ - -/* - * Defined if we have the backtrace() call for retrieving a stack trace. - * Needed for CallStack to operate; if not defined, CallStack is - * non-functional. - */ -#define HAVE_BACKTRACE 0 - -/* - * Defined if we have the dladdr() call for retrieving the symbol associated - * with a memory address. If not defined, stack crawls will not have symbolic - * information. - */ -#define HAVE_DLADDR 0 - -/* - * Defined if we have the cxxabi.h header for demangling C++ symbols. If - * not defined, stack crawls will be displayed with raw mangled symbols - */ -#define HAVE_CXXABI 0 - -/* - * Defined if we have the gettid() system call. - */ -/* #define HAVE_GETTID */ - - -/* - * Add any extra platform-specific defines here. - */ -#define _THREAD_SAFE - -/* - * Define if we have <malloc.h> header - */ -/* #define HAVE_MALLOC_H */ - -/* - * Define if tm struct has tm_gmtoff field - */ -#define HAVE_TM_GMTOFF 1 - -/* - * Define if dirent struct has d_type field - */ -#define HAVE_DIRENT_D_TYPE 1 - -/* - * Define if we have madvise() in <sys/mman.h> - */ -#define HAVE_MADVISE 1 - -/* - * Define if we include <sys/mount.h> for statfs() - */ -#define INCLUDE_SYS_MOUNT_FOR_STATFS 1 - -/* - * What CPU architecture does this platform use? - */ -#if (defined(__ppc__) || defined(__ppc64__)) -# define ARCH_PPC -#elif (defined(__i386__) || defined(__x86_64__)) -# define ARCH_X86 -#endif - -/* - * sprintf() format string for shared library naming. - */ -#define OS_SHARED_LIB_FORMAT_STR "lib%s.dylib" - -/* - * type for the third argument to mincore(). - */ -#define MINCORE_POINTER_TYPE char * - -/* - * The default path separator for the platform - */ -#define OS_PATH_SEPARATOR '/' - -/* - * Is the filesystem case sensitive? - * - * For tools apps, we'll treat is as not case sensitive. - */ -/* #define OS_CASE_SENSITIVE */ - -/* - * Define if <sys/socket.h> exists. - */ -#define HAVE_SYS_SOCKET_H 1 - -/* - * Define if the strlcpy() function exists on the system. - */ -#define HAVE_STRLCPY 1 - -/* - * Define if the open_memstream() function exists on the system. - */ -/* #define HAVE_OPEN_MEMSTREAM 1 */ - -/* - * Define if the BSD funopen() function exists on the system. - */ -#define HAVE_FUNOPEN 1 - -/* - * Define if writev() exists - */ -#define HAVE_WRITEV 1 - -/* - * Define if <stdint.h> exists. - */ -#define HAVE_STDINT_H 1 - -/* - * Define if <stdbool.h> exists. - */ -#define HAVE_STDBOOL_H 1 - -/* - * Define if <sched.h> exists. - */ -#define HAVE_SCHED_H 1 - -/* - * Define if pread() exists - */ -#define HAVE_PREAD 1 - -/* - * Define if we have st_mtim in struct stat - */ -#define HAVE_STAT_ST_MTIM 1 - -/* - * Define if printf() supports %zd for size_t arguments - */ -#define HAVE_PRINTF_ZD 1 - -/* - * Define to 1 if <stdlib.h> provides qsort_r() with a BSD style function prototype. - */ -#define HAVE_BSD_QSORT_R 1 - -/* - * Define to 1 if <stdlib.h> provides qsort_r() with a GNU style function prototype. - */ -#define HAVE_GNU_QSORT_R 0 - -#endif /*_ANDROID_CONFIG_H*/ diff --git a/include/arch/freebsd-x86/AndroidConfig.h b/include/arch/freebsd-x86/AndroidConfig.h deleted file mode 100644 index 4bc5559..0000000 --- a/include/arch/freebsd-x86/AndroidConfig.h +++ /dev/null @@ -1,376 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -/* - * Android config -- "FreeBSD". Used for desktop x86 FreeBSD. - */ -#ifndef _ANDROID_CONFIG_H -#define _ANDROID_CONFIG_H - -/* - * make sure we are building for FreeBSD - */ -#ifndef OS_FREEBSD -#define OS_FREEBSD -#endif -/* - * =========================================================================== - * !!! IMPORTANT !!! - * =========================================================================== - * - * This file is included by ALL C/C++ source files. Don't put anything in - * here unless you are absolutely certain it can't go anywhere else. - * - * Any C++ stuff must be wrapped with "#ifdef __cplusplus". Do not use "//" - * comments. - */ - -/* - * Threading model. Choose one: - * - * HAVE_PTHREADS - use the pthreads library. - * HAVE_WIN32_THREADS - use Win32 thread primitives. - * -- combine HAVE_CREATETHREAD, HAVE_CREATEMUTEX, and HAVE__BEGINTHREADEX - */ -#define HAVE_PTHREADS - -/* - * Do we have the futex syscall? - */ -/* #define HAVE_FUTEX */ - -/* - * Process creation model. Choose one: - * - * HAVE_FORKEXEC - use fork() and exec() - * HAVE_WIN32_PROC - use CreateProcess() - */ -#define HAVE_FORKEXEC - -/* - * Process out-of-memory adjustment. Set if running on Linux, - * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory - * badness adjustment. - */ -/* #define HAVE_OOM_ADJ */ - -/* - * IPC model. Choose one: - * - * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget). - * HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap). - * HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping). - * HAVE_ANDROID_IPC - use Android versions (?, mmap). - */ -#define HAVE_SYSV_IPC - -/* - * Memory-mapping model. Choose one: - * - * HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h - * HAVE_WIN32_FILEMAP - use Win32 filemaps - */ -#define HAVE_POSIX_FILEMAP - -/* - * Define this if you have <termio.h> - */ -/* #define HAVE_TERMIO_H */ - -/* - * Define this if you have <sys/sendfile.h> - */ -/* #define HAVE_SYS_SENDFILE_H 1 */ - -/* - * Define this if you build against MSVCRT.DLL - */ -/* #define HAVE_MS_C_RUNTIME */ - -/* - * Define this if you have sys/uio.h - */ -#define HAVE_SYS_UIO_H - -/* - * Define this if your platforms implements symbolic links - * in its filesystems - */ -#define HAVE_SYMLINKS - -/* - * Define this if we have localtime_r(). - */ -#define HAVE_LOCALTIME_R 1 - -/* - * Define this if we have gethostbyname_r(). - */ -/* #define HAVE_GETHOSTBYNAME_R */ - -/* - * Define this if we have ioctl(). - */ -#define HAVE_IOCTL - -/* - * Define this if we want to use WinSock. - */ -/* #define HAVE_WINSOCK */ - -/* - * Define this if have clock_gettime() and friends - * - * Desktop Linux has this in librt, but it's broken in goobuntu, yielding - * mildly or wildly inaccurate results. - */ -#define HAVE_POSIX_CLOCKS - -/* - * Define this if we have pthread_cond_timedwait_monotonic() and - * clock_gettime(CLOCK_MONOTONIC). - */ -/* #define HAVE_TIMEDWAIT_MONOTONIC */ - -/* - * Define this if we have linux style epoll() - */ -/* #define HAVE_EPOLL */ - -/* - * Endianness of the target machine. Choose one: - * - * HAVE_ENDIAN_H -- have endian.h header we can include. - * HAVE_LITTLE_ENDIAN -- we are little endian. - * HAVE_BIG_ENDIAN -- we are big endian. - */ -/* #define HAVE_ENDIAN_H */ -#define HAVE_LITTLE_ENDIAN - -/* - * Define this if you have sys/endian.h - * NOTE: mutually exclusive with HAVE_ENDIAN_H - */ -#define HAVE_SYS_ENDIAN_H - -/* - * We need to choose between 32-bit and 64-bit off_t. All of our code should - * agree on the same size. For desktop systems, use 64-bit values, - * because some of our libraries (e.g. wxWidgets) expect to be built that way. - */ -#define _FILE_OFFSET_BITS 64 -#define _LARGEFILE_SOURCE 1 - -/* - * Define if platform has off64_t (and lseek64 and other xxx64 functions) - */ -/* #define HAVE_OFF64_T */ - -/* - * Defined if we have the backtrace() call for retrieving a stack trace. - * Needed for CallStack to operate; if not defined, CallStack is - * non-functional. - */ -#define HAVE_BACKTRACE 0 - -/* - * Defined if we have the dladdr() call for retrieving the symbol associated - * with a memory address. If not defined, stack crawls will not have symbolic - * information. - */ -#define HAVE_DLADDR 1 - -/* - * Defined if we have the cxxabi.h header for demangling C++ symbols. If - * not defined, stack crawls will be displayed with raw mangled symbols - */ -#define HAVE_CXXABI 0 - -/* - * Defined if we have the gettid() system call. - */ -/* #define HAVE_GETTID */ - -/* - * Defined if we have the sched_setscheduler() call - */ -#define HAVE_SCHED_SETSCHEDULER - -/* - * Add any extra platform-specific defines here. - */ - -/* - * Define if we have <malloc.h> header - */ -#define HAVE_MALLOC_H - -/* - * Define if we have Linux-style non-filesystem Unix Domain Sockets - */ - -/* - * What CPU architecture does this platform use? - */ -#define ARCH_X86 - - -/* - * Define if we have Linux's inotify in <sys/inotify.h>. - */ -/*#define HAVE_INOTIFY 1*/ - -/* - * Define if we have madvise() in <sys/mman.h> - */ -#define HAVE_MADVISE 1 - -/* - * Define if tm struct has tm_gmtoff field - */ -#define HAVE_TM_GMTOFF 1 - -/* - * Define if dirent struct has d_type field - */ -#define HAVE_DIRENT_D_TYPE 1 - -/* - * Define if libc includes Android system properties implementation. - */ -/* #define HAVE_LIBC_SYSTEM_PROPERTIES */ - -/* - * Define if system provides a system property server (should be - * mutually exclusive with HAVE_LIBC_SYSTEM_PROPERTIES). - */ -#define HAVE_SYSTEM_PROPERTY_SERVER - -/* - * sprintf() format string for shared library naming. - */ -#define OS_SHARED_LIB_FORMAT_STR "lib%s.so" - -/* - * type for the third argument to mincore(). - */ -#define MINCORE_POINTER_TYPE char * - -/* - * Do we have the sigaction flag SA_NOCLDWAIT? - */ -#define HAVE_SA_NOCLDWAIT - -/* - * Define if we include <sys/mount.h> for statfs() - */ -#define INCLUDE_SYS_MOUNT_FOR_STATFS 1 - -/* - * The default path separator for the platform - */ -#define OS_PATH_SEPARATOR '/' - -/* - * Is the filesystem case sensitive? - */ -#define OS_CASE_SENSITIVE - -/* - * Define if <sys/socket.h> exists. - */ -#define HAVE_SYS_SOCKET_H 1 - -/* - * Define if the strlcpy() function exists on the system. - */ -#define HAVE_STRLCPY 1 - -/* - * Define if the open_memstream() function exists on the system. - */ -/* #define HAVE_OPEN_MEMSTREAM 1 */ - -/* - * Define if the BSD funopen() function exists on the system. - */ -#define HAVE_FUNOPEN 1 - -/* - * Define if prctl() exists - */ -/* #define HAVE_PRCTL 1 */ - -/* - * Define if writev() exists - */ -#define HAVE_WRITEV 1 - -/* - * Define if <alloca.h> does not exist - * NOTE: <alloca.h> defines alloca() which - * on FreeBSD is defined in <stdlib.h> - */ -#define HAVE_NO_ALLOCA_H - -/* - * Defines CLOCK_PROCESS_CPUTIME_ID for clock_gettime() - * XXX: CLOCK_PROF seems to be commonly used replacement - */ -#ifndef CLOCK_PROCESS_CPUTIME_ID -#define CLOCK_PROCESS_CPUTIME_ID CLOCK_PROF -#endif - -/* - * Define if <stdint.h> exists. - */ -/* #define HAVE_STDINT_H */ - -/* - * Define if <stdbool.h> exists. - */ -/* #define HAVE_STDBOOL_H */ - -/* - * Define if <sched.h> exists. - */ -#define HAVE_SCHED_H 1 - -/* - * Define if pread() exists - */ -#define HAVE_PREAD 1 -/* - * Define if we have st_mtim in struct stat - */ -#define HAVE_STAT_ST_MTIM 1 - -/* - * Define if printf() supports %zd for size_t arguments - */ -#define HAVE_PRINTF_ZD 1 - -/* - * Define to 1 if <stdlib.h> provides qsort_r() with a BSD style function prototype. - */ -#define HAVE_BSD_QSORT_R 1 - -/* - * Define to 1 if <stdlib.h> provides qsort_r() with a GNU style function prototype. - */ -#define HAVE_GNU_QSORT_R 0 - -#endif /*_ANDROID_CONFIG_H*/ diff --git a/include/arch/linux-arm/AndroidConfig.h b/include/arch/linux-arm/AndroidConfig.h deleted file mode 100644 index 233752b..0000000 --- a/include/arch/linux-arm/AndroidConfig.h +++ /dev/null @@ -1,374 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -/* - * Android config -- "android-arm". Used for ARM device builds. - */ -#ifndef _ANDROID_CONFIG_H -#define _ANDROID_CONFIG_H - -/* - * =========================================================================== - * !!! IMPORTANT !!! - * =========================================================================== - * - * This file is included by ALL C/C++ source files. Don't put anything in - * here unless you are absolutely certain it can't go anywhere else. - * - * Any C++ stuff must be wrapped with "#ifdef __cplusplus". Do not use "//" - * comments. - */ - -/* - * Threading model. Choose one: - * - * HAVE_PTHREADS - use the pthreads library. - * HAVE_WIN32_THREADS - use Win32 thread primitives. - * -- combine HAVE_CREATETHREAD, HAVE_CREATEMUTEX, and HAVE__BEGINTHREADEX - */ -#define HAVE_PTHREADS - -/* - * Do we have pthread_setname_np()? - * - * (HAVE_PTHREAD_SETNAME_NP is used by WebKit to enable a function with - * the same name but different parameters, so we can't use that here.) - */ -#define HAVE_ANDROID_PTHREAD_SETNAME_NP - -/* - * Do we have the futex syscall? - */ -#define HAVE_FUTEX - -/* - * Define if we already have the futex wrapper functions defined. Yes if - * compiling against bionic. - */ -#define HAVE_FUTEX_WRAPPERS 1 - -/* - * Process creation model. Choose one: - * - * HAVE_FORKEXEC - use fork() and exec() - * HAVE_WIN32_PROC - use CreateProcess() - */ -#define HAVE_FORKEXEC - -/* - * Process out-of-memory adjustment. Set if running on Linux, - * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory - * badness adjustment. - */ -#define HAVE_OOM_ADJ - -/* - * IPC model. Choose one: - * - * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget). - * HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap). - * HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping). - * HAVE_ANDROID_IPC - use Android versions (?, mmap). - */ -#define HAVE_ANDROID_IPC - -/* - * Memory-mapping model. Choose one: - * - * HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h - * HAVE_WIN32_FILEMAP - use Win32 filemaps - */ -#define HAVE_POSIX_FILEMAP - -/* - * Define this if you have <termio.h> - */ -#define HAVE_TERMIO_H 1 - -/* - * Define this if you have <sys/sendfile.h> - */ -#define HAVE_SYS_SENDFILE_H 1 - -/* - * Define this if you build against MSVCRT.DLL - */ -/* #define HAVE_MS_C_RUNTIME */ - -/* - * Define this if you have sys/uio.h - */ -#define HAVE_SYS_UIO_H 1 - -/* - * Define this if your platforms implements symbolic links - * in its filesystems - */ -#define HAVE_SYMLINKS - -/* - * Define this if we have localtime_r(). - */ -/* #define HAVE_LOCALTIME_R 1 */ - -/* - * Define this if we have gethostbyname_r(). - */ -/* #define HAVE_GETHOSTBYNAME_R */ - -/* - * Define this if we have ioctl(). - */ -#define HAVE_IOCTL - -/* - * Define this if we want to use WinSock. - */ -/* #define HAVE_WINSOCK */ - -/* - * Define this if have clock_gettime() and friends - */ -#define HAVE_POSIX_CLOCKS - -/* - * Define this if we have pthread_cond_timedwait_monotonic() and - * clock_gettime(CLOCK_MONOTONIC). - */ -#define HAVE_TIMEDWAIT_MONOTONIC - -/* - * Define this if we have linux style epoll() - */ -#define HAVE_EPOLL - -/* - * Endianness of the target machine. Choose one: - * - * HAVE_ENDIAN_H -- have endian.h header we can include. - * HAVE_LITTLE_ENDIAN -- we are little endian. - * HAVE_BIG_ENDIAN -- we are big endian. - */ -#define HAVE_ENDIAN_H -#define HAVE_LITTLE_ENDIAN - -/* - * We need to choose between 32-bit and 64-bit off_t. All of our code should - * agree on the same size. For desktop systems, use 64-bit values, - * because some of our libraries (e.g. wxWidgets) expect to be built that way. - */ -/* #define _FILE_OFFSET_BITS 64 */ -/* #define _LARGEFILE_SOURCE 1 */ - -/* - * Define if platform has off64_t (and lseek64 and other xxx64 functions) - */ -#define HAVE_OFF64_T - -/* - * Defined if we have the backtrace() call for retrieving a stack trace. - * Needed for CallStack to operate; if not defined, CallStack is - * non-functional. - */ -#define HAVE_BACKTRACE 0 - -/* - * Defined if we have the dladdr() call for retrieving the symbol associated - * with a memory address. If not defined, stack crawls will not have symbolic - * information. - */ -#define HAVE_DLADDR 1 - -/* - * Defined if we have the cxxabi.h header for demangling C++ symbols. If - * not defined, stack crawls will be displayed with raw mangled symbols - */ -#define HAVE_CXXABI 0 - -/* - * Defined if we have the gettid() system call. - */ -#define HAVE_GETTID - -/* - * Defined if we have the sched_setscheduler() call - */ -#define HAVE_SCHED_SETSCHEDULER - -/* - * Add any extra platform-specific defines here. - */ -#ifndef __linux__ -#define __linux__ -#endif - -/* - * Define if we have <malloc.h> header - */ -#define HAVE_MALLOC_H - -/* - * Define if we're running on *our* linux on device or emulator. - */ -#define HAVE_ANDROID_OS 1 - -/* - * Define if we have Linux-style non-filesystem Unix Domain Sockets - */ -#define HAVE_LINUX_LOCAL_SOCKET_NAMESPACE 1 - -/* - * Define if we have Linux's inotify in <sys/inotify.h>. - */ -#define HAVE_INOTIFY 1 - -/* - * Define if we have madvise() in <sys/mman.h> - */ -#define HAVE_MADVISE 1 - -/* - * Define if tm struct has tm_gmtoff field - */ -#define HAVE_TM_GMTOFF 1 - -/* - * Define if dirent struct has d_type field - */ -#define HAVE_DIRENT_D_TYPE 1 - -/* - * Define if libc includes Android system properties implementation. - */ -#define HAVE_LIBC_SYSTEM_PROPERTIES 1 - -/* - * Define if system provides a system property server (should be - * mutually exclusive with HAVE_LIBC_SYSTEM_PROPERTIES). - */ -/* #define HAVE_SYSTEM_PROPERTY_SERVER */ - -/* - * What CPU architecture does this platform use? - */ -#define ARCH_ARM - -/* - * Define if the size of enums is as short as possible, - */ -/* #define HAVE_SHORT_ENUMS */ - -/* - * sprintf() format string for shared library naming. - */ -#define OS_SHARED_LIB_FORMAT_STR "lib%s.so" - -/* - * Do we have __memcmp16()? - */ -#define HAVE__MEMCMP16 1 - -/* - * type for the third argument to mincore(). - */ -#define MINCORE_POINTER_TYPE unsigned char * - -/* - * Do we have the sigaction flag SA_NOCLDWAIT? - */ -#define HAVE_SA_NOCLDWAIT - -/* - * The default path separator for the platform - */ -#define OS_PATH_SEPARATOR '/' - -/* - * Is the filesystem case sensitive? - */ -#define OS_CASE_SENSITIVE - -/* - * Define if <sys/socket.h> exists. - */ -#define HAVE_SYS_SOCKET_H 1 - -/* - * Define if the strlcpy() function exists on the system. - */ -#define HAVE_STRLCPY 1 - -/* - * Define if the open_memstream() function exists on the system. - */ -/* #define HAVE_OPEN_MEMSTREAM 1 */ - -/* - * Define if the BSD funopen() function exists on the system. - */ -#define HAVE_FUNOPEN 1 - -/* - * Define if prctl() exists - */ -#define HAVE_PRCTL 1 - -/* - * Define if writev() exists - */ -#define HAVE_WRITEV 1 - -/* - * Define if <stdint.h> exists. - */ -#define HAVE_STDINT_H 1 - -/* - * Define if <stdbool.h> exists. - */ -#define HAVE_STDBOOL_H 1 - -/* - * Define if <sched.h> exists. - */ -#define HAVE_SCHED_H 1 - -/* - * Define if pread() exists - */ -#define HAVE_PREAD 1 - -/* - * Define if we have st_mtim in struct stat - */ -#define HAVE_STAT_ST_MTIM 1 - -/* - * Define if printf() supports %zd for size_t arguments - */ -#define HAVE_PRINTF_ZD 1 - -/* - * Define to 1 if <stdlib.h> provides qsort_r() with a BSD style function prototype. - */ -#define HAVE_BSD_QSORT_R 0 - -/* - * Define to 1 if <stdlib.h> provides qsort_r() with a GNU style function prototype. - */ -#define HAVE_GNU_QSORT_R 0 - -#endif /* _ANDROID_CONFIG_H */ diff --git a/include/arch/linux-mips/AndroidConfig.h b/include/arch/linux-mips/AndroidConfig.h deleted file mode 100644 index 2d51dc7..0000000 --- a/include/arch/linux-mips/AndroidConfig.h +++ /dev/null @@ -1,386 +0,0 @@ -/* - * Copyright (C) 2010 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. - */ - -/* - * Android config -- "android-mips". Used for MIPS device builds. - */ -#ifndef _ANDROID_CONFIG_H -#define _ANDROID_CONFIG_H - -/* - * =========================================================================== - * !!! IMPORTANT !!! - * =========================================================================== - * - * This file is included by ALL C/C++ source files. Don't put anything in - * here unless you are absolutely certain it can't go anywhere else. - * - * Any C++ stuff must be wrapped with "#ifdef __cplusplus". Do not use "//" - * comments. - */ - -/* - * Threading model. Choose one: - * - * HAVE_PTHREADS - use the pthreads library. - * HAVE_WIN32_THREADS - use Win32 thread primitives. - * -- combine HAVE_CREATETHREAD, HAVE_CREATEMUTEX, and HAVE__BEGINTHREADEX - */ -#define HAVE_PTHREADS - -/* - * Do we have pthread_setname_np()? - * - * (HAVE_PTHREAD_SETNAME_NP is used by WebKit to enable a function with - * the same name but different parameters, so we can't use that here.) - */ -#define HAVE_ANDROID_PTHREAD_SETNAME_NP - -/* - * Do we have the futex syscall? - */ -#define HAVE_FUTEX - -/* - * Define if we already have the futex wrapper functions defined. Yes if - * compiling against bionic. - */ -#define HAVE_FUTEX_WRAPPERS 1 - -/* - * Process creation model. Choose one: - * - * HAVE_FORKEXEC - use fork() and exec() - * HAVE_WIN32_PROC - use CreateProcess() - */ -#define HAVE_FORKEXEC - -/* - * Process out-of-memory adjustment. Set if running on Linux, - * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory - * badness adjustment. - */ -#define HAVE_OOM_ADJ - -/* - * IPC model. Choose one: - * - * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget). - * HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap). - * HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping). - * HAVE_ANDROID_IPC - use Android versions (?, mmap). - */ -#define HAVE_ANDROID_IPC - -/* - * Memory-mapping model. Choose one: - * - * HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h - * HAVE_WIN32_FILEMAP - use Win32 filemaps - */ -#define HAVE_POSIX_FILEMAP - -/* - * Define this if you have <termio.h> - */ -#define HAVE_TERMIO_H 1 - -/* - * Define this if you have <sys/sendfile.h> - */ -#define HAVE_SYS_SENDFILE_H 1 - -/* - * Define this if you build against MSVCRT.DLL - */ -/* #define HAVE_MS_C_RUNTIME */ - -/* - * Define this if you have sys/uio.h - */ -#define HAVE_SYS_UIO_H 1 - -/* - * Define this if your platforms implements symbolic links - * in its filesystems - */ -#define HAVE_SYMLINKS - -/* - * Define this if we have localtime_r(). - */ -/* #define HAVE_LOCALTIME_R */ - -/* - * Define this if we have gethostbyname_r(). - */ -/* #define HAVE_GETHOSTBYNAME_R */ - -/* - * Define this if we have ioctl(). - */ -#define HAVE_IOCTL - -/* - * Define this if we want to use WinSock. - */ -/* #define HAVE_WINSOCK */ - -/* - * Define this if have clock_gettime() and friends - */ -#define HAVE_POSIX_CLOCKS - -/* - * Define this if we have pthread_cond_timedwait_monotonic() and - * clock_gettime(CLOCK_MONOTONIC). - */ -#define HAVE_TIMEDWAIT_MONOTONIC - -/* - * Define this if we have linux style epoll() - */ -#define HAVE_EPOLL - -/* - * Endianness of the target machine. Choose one: - * - * HAVE_ENDIAN_H -- have endian.h header we can include. - * HAVE_LITTLE_ENDIAN -- we are little endian. - * HAVE_BIG_ENDIAN -- we are big endian. - */ -#define HAVE_ENDIAN_H -#if defined(__MIPSEB__) -#define HAVE_BIG_ENDIAN -#endif -#if defined(__MIPSEL__) -#define HAVE_LITTLE_ENDIAN -#endif - -/* - * We need to choose between 32-bit and 64-bit off_t. All of our code should - * agree on the same size. For desktop systems, use 64-bit values, - * because some of our libraries (e.g. wxWidgets) expect to be built that way. - */ -/* #define _FILE_OFFSET_BITS 64 */ -/* #define _LARGEFILE_SOURCE 1 */ - -/* - * Define if platform has off64_t (and lseek64 and other xxx64 functions) - */ -#define HAVE_OFF64_T - -/* - * Defined if we have the backtrace() call for retrieving a stack trace. - * Needed for CallStack to operate; if not defined, CallStack is - * non-functional. - */ -#define HAVE_BACKTRACE 0 - -/* - * Defined if we have the dladdr() call for retrieving the symbol associated - * with a memory address. If not defined, stack crawls will not have symbolic - * information. - */ -#define HAVE_DLADDR 1 - -/* - * Defined if we have the cxxabi.h header for demangling C++ symbols. If - * not defined, stack crawls will be displayed with raw mangled symbols - */ -#define HAVE_CXXABI 0 - -/* - * Defined if we have the gettid() system call. - */ -#define HAVE_GETTID - -/* - * Defined if we have the sched_setscheduler() call - */ -#define HAVE_SCHED_SETSCHEDULER - -/* - * Add any extra platform-specific defines here. - */ -#ifndef __linux__ -#define __linux__ 1 -#endif - -#ifndef __linux -#define __linux 1 -#endif - -#ifdef __unix__ -#undef __unix__ -#endif - -#ifdef __unix -#undef __unix -#endif - -/* - * Define if we have <malloc.h> header - */ -#define HAVE_MALLOC_H - -/* - * Define if we're running on *our* linux on device or emulator. - */ -#define HAVE_ANDROID_OS 1 - -/* - * Define if we have Linux-style non-filesystem Unix Domain Sockets - */ -#define HAVE_LINUX_LOCAL_SOCKET_NAMESPACE 1 - -/* - * Define if we have Linux's inotify in <sys/inotify.h>. - */ -#define HAVE_INOTIFY 1 - -/* - * Define if we have madvise() in <sys/mman.h> - */ -#define HAVE_MADVISE 1 - -/* - * Define if tm struct has tm_gmtoff field - */ -#define HAVE_TM_GMTOFF 1 - -/* - * Define if dirent struct has d_type field - */ -#define HAVE_DIRENT_D_TYPE 1 - -/* - * Define if libc includes Android system properties implementation. - */ -#define HAVE_LIBC_SYSTEM_PROPERTIES 1 - -/* - * Define if system provides a system property server (should be - * mutually exclusive with HAVE_LIBC_SYSTEM_PROPERTIES). - */ -/* #define HAVE_SYSTEM_PROPERTY_SERVER */ - -/* - * What CPU architecture does this platform use? - */ -#define ARCH_MIPS 1 - -/* - * Define if the size of enums is as short as possible, - */ -/* #define HAVE_SHORT_ENUMS */ - -/* - * sprintf() format string for shared library naming. - */ -#define OS_SHARED_LIB_FORMAT_STR "lib%s.so" - -/* - * Do we have __memcmp16()? - */ -#define HAVE__MEMCMP16 1 - -/* - * type for the third argument to mincore(). - */ -#define MINCORE_POINTER_TYPE unsigned char * - -/* - * Do we have the sigaction flag SA_NOCLDWAIT? - */ -#define HAVE_SA_NOCLDWAIT - -/* - * The default path separator for the platform - */ -#define OS_PATH_SEPARATOR '/' - -/* - * Is the filesystem case sensitive? - */ -#define OS_CASE_SENSITIVE - -/* - * Define if <sys/socket.h> exists. - */ -#define HAVE_SYS_SOCKET_H 1 - -/* - * Define if the strlcpy() function exists on the system. - */ -#define HAVE_STRLCPY 1 - -/* - * Define if the open_memstream() function exists on the system. - */ -/* #define HAVE_OPEN_MEMSTREAM 1 */ - -/* - * Define if the BSD funopen() function exists on the system. - */ -#define HAVE_FUNOPEN 1 - -/* - * Define if prctl() exists - */ -#define HAVE_PRCTL 1 - -/* - * Define if writev() exists - */ -#define HAVE_WRITEV 1 - -/* - * Define if <stdint.h> exists. - */ -#define HAVE_STDINT_H 1 - -/* - * Define if <stdbool.h> exists. - */ -#define HAVE_STDBOOL_H 1 - -/* - * Define if <sched.h> exists. - */ -#define HAVE_SCHED_H 1 - -/* - * Define if pread() exists - */ -#define HAVE_PREAD 1 - -/* - * Define if we have st_mtim in struct stat - */ -#define HAVE_STAT_ST_MTIM 1 - -/* - * Define if printf() supports %zd for size_t arguments - */ -#define HAVE_PRINTF_ZD 1 - -/* - * Whether or not _Unwind_Context is defined as a struct. - */ -#define HAVE_UNWIND_CONTEXT_STRUCT 1 - -#endif /* _ANDROID_CONFIG_H */ diff --git a/include/arch/linux-ppc/AndroidConfig.h b/include/arch/linux-ppc/AndroidConfig.h deleted file mode 100644 index ae2569b..0000000 --- a/include/arch/linux-ppc/AndroidConfig.h +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright (C) 2010 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. - */ - -/* - * Android config -- "Linux". Used for desktop ppc Linux. - */ -#ifndef _ANDROID_CONFIG_H -#define _ANDROID_CONFIG_H - -/* - * =========================================================================== - * !!! IMPORTANT !!! - * =========================================================================== - * - * This file is included by ALL C/C++ source files. Don't put anything in - * here unless you are absolutely certain it can't go anywhere else. - * - * Any C++ stuff must be wrapped with "#ifdef __cplusplus". Do not use "//" - * comments. - */ - -/* - * Threading model. Choose one: - * - * HAVE_PTHREADS - use the pthreads library. - * HAVE_WIN32_THREADS - use Win32 thread primitives. - * -- combine HAVE_CREATETHREAD, HAVE_CREATEMUTEX, and HAVE__BEGINTHREADEX - */ -#define HAVE_PTHREADS - -/* - * Do we have the futex syscall? - */ - -#define HAVE_FUTEX - -/* - * Process creation model. Choose one: - * - * HAVE_FORKEXEC - use fork() and exec() - * HAVE_WIN32_PROC - use CreateProcess() - */ -#define HAVE_FORKEXEC - -/* - * Process out-of-memory adjustment. Set if running on Linux, - * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory - * badness adjustment. - */ -#define HAVE_OOM_ADJ - -/* - * IPC model. Choose one: - * - * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget). - * HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap). - * HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping). - * HAVE_ANDROID_IPC - use Android versions (?, mmap). - */ -#define HAVE_SYSV_IPC - -/* - * Memory-mapping model. Choose one: - * - * HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h - * HAVE_WIN32_FILEMAP - use Win32 filemaps - */ -#define HAVE_POSIX_FILEMAP - -/* - * Define this if you have <termio.h> - */ -#define HAVE_TERMIO_H 1 - -/* - * Define this if you have <sys/sendfile.h> - */ -#define HAVE_SYS_SENDFILE_H 1 - -/* - * Define this if you build against MSVCRT.DLL - */ -/* #define HAVE_MS_C_RUNTIME */ - -/* - * Define this if you have sys/uio.h - */ -#define HAVE_SYS_UIO_H 1 - -/* - * Define this if your platforms implements symbolic links - * in its filesystems - */ -#define HAVE_SYMLINKS - -/* - * Define this if we have localtime_r(). - */ -#define HAVE_LOCALTIME_R 1 - -/* - * Define this if we have gethostbyname_r(). - */ -#define HAVE_GETHOSTBYNAME_R - -/* - * Define this if we have ioctl(). - */ -#define HAVE_IOCTL - -/* - * Define this if we want to use WinSock. - */ -/* #define HAVE_WINSOCK */ - -/* - * Define this if have clock_gettime() and friends - * - * Desktop Linux has this in librt, but it's broken in goobuntu, yielding - * mildly or wildly inaccurate results. - */ -/*#define HAVE_POSIX_CLOCKS*/ - -/* - * Define this if we have pthread_cond_timedwait_monotonic() and - * clock_gettime(CLOCK_MONOTONIC). - */ -/* #define HAVE_TIMEDWAIT_MONOTONIC */ - -/* - * Define this if we have linux style epoll() - */ -#define HAVE_EPOLL - -/* - * Endianness of the target machine. Choose one: - * - * HAVE_ENDIAN_H -- have endian.h header we can include. - * HAVE_LITTLE_ENDIAN -- we are little endian. - * HAVE_BIG_ENDIAN -- we are big endian. - */ -#define HAVE_ENDIAN_H -#define HAVE_BIG_ENDIAN - -/* - * We need to choose between 32-bit and 64-bit off_t. All of our code should - * agree on the same size. For desktop systems, use 64-bit values, - * because some of our libraries (e.g. wxWidgets) expect to be built that way. - */ -#define _FILE_OFFSET_BITS 64 -#define _LARGEFILE_SOURCE 1 - -/* - * Define if platform has off64_t (and lseek64 and other xxx64 functions) - */ -#define HAVE_OFF64_T - -/* - * Defined if we have the backtrace() call for retrieving a stack trace. - * Needed for CallStack to operate; if not defined, CallStack is - * non-functional. - */ -#define HAVE_BACKTRACE 1 - -/* - * Defined if we have the dladdr() call for retrieving the symbol associated - * with a memory address. If not defined, stack crawls will not have symbolic - * information. - */ -#define HAVE_DLADDR 1 - -/* - * Defined if we have the cxxabi.h header for demangling C++ symbols. If - * not defined, stack crawls will be displayed with raw mangled symbols - */ -#define HAVE_CXXABI 0 - -/* - * Defined if we have the gettid() system call. - */ -/* #define HAVE_GETTID */ - -/* - * Defined if we have the sched_setscheduler() call - */ -#define HAVE_SCHED_SETSCHEDULER - -/* - * Add any extra platform-specific defines here. - */ - -/* - * Define if we have <malloc.h> header - */ -#define HAVE_MALLOC_H - -/* - * Define if we have Linux-style non-filesystem Unix Domain Sockets - */ - -/* - * What CPU architecture does this platform use? - */ -#define ARCH_PPC - - -/* - * Define if we have Linux's inotify in <sys/inotify.h>. - */ -/*#define HAVE_INOTIFY 1*/ - -/* - * Define if we have madvise() in <sys/mman.h> - */ -#define HAVE_MADVISE 1 - -/* - * Define if tm struct has tm_gmtoff field - */ -#define HAVE_TM_GMTOFF 1 - -/* - * Define if dirent struct has d_type field - */ -#define HAVE_DIRENT_D_TYPE 1 - -/* - * Define if libc includes Android system properties implementation. - */ -/* #define HAVE_LIBC_SYSTEM_PROPERTIES */ - -/* - * Define if system provides a system property server (should be - * mutually exclusive with HAVE_LIBC_SYSTEM_PROPERTIES). - */ -#define HAVE_SYSTEM_PROPERTY_SERVER - -/* - * sprintf() format string for shared library naming. - */ -#define OS_SHARED_LIB_FORMAT_STR "lib%s.so" - -/* - * type for the third argument to mincore(). - */ -#define MINCORE_POINTER_TYPE unsigned char * - -/* - * Do we have the sigaction flag SA_NOCLDWAIT? - */ -#define HAVE_SA_NOCLDWAIT - -/* - * The default path separator for the platform - */ -#define OS_PATH_SEPARATOR '/' - -/* - * Is the filesystem case sensitive? - */ -#define OS_CASE_SENSITIVE - -/* - * Define if <sys/socket.h> exists. - */ -#define HAVE_SYS_SOCKET_H 1 - -/* - * Define if the strlcpy() function exists on the system. - */ -/* #define HAVE_STRLCPY 1 */ - -/* - * Define if the open_memstream() function exists on the system. - */ -#define HAVE_OPEN_MEMSTREAM 1 - -/* - * Define if the BSD funopen() function exists on the system. - */ -/* #define HAVE_FUNOPEN 1 */ - -/* - * Define if prctl() exists - */ -#define HAVE_PRCTL 1 - -/* - * Define if writev() exists - */ -#define HAVE_WRITEV 1 - -/* - * Define if <stdint.h> exists. - */ -#define HAVE_STDINT_H 1 - -/* - * Define if <stdbool.h> exists. - */ -#define HAVE_STDBOOL_H 1 - -/* - * Define if <sched.h> exists. - */ -#define HAVE_SCHED_H 1 - -/* - * Define if pread() exists - */ -#define HAVE_PREAD 1 - -/* - * Define to 1 if <stdlib.h> provides qsort_r() with a BSD style function prototype. - */ -#define HAVE_BSD_QSORT_R 0 - -/* - * Define to 1 if <stdlib.h> provides qsort_r() with a GNU style function prototype. - */ -#define HAVE_GNU_QSORT_R 1 - -#endif /*_ANDROID_CONFIG_H*/ diff --git a/include/arch/linux-x86/AndroidConfig.h b/include/arch/linux-x86/AndroidConfig.h deleted file mode 100644 index 431a54b..0000000 --- a/include/arch/linux-x86/AndroidConfig.h +++ /dev/null @@ -1,350 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -/* - * Android config -- "Linux". Used for desktop x86 Linux. - */ -#ifndef _ANDROID_CONFIG_H -#define _ANDROID_CONFIG_H - -/* - * =========================================================================== - * !!! IMPORTANT !!! - * =========================================================================== - * - * This file is included by ALL C/C++ source files. Don't put anything in - * here unless you are absolutely certain it can't go anywhere else. - * - * Any C++ stuff must be wrapped with "#ifdef __cplusplus". Do not use "//" - * comments. - */ - -/* - * Threading model. Choose one: - * - * HAVE_PTHREADS - use the pthreads library. - * HAVE_WIN32_THREADS - use Win32 thread primitives. - * -- combine HAVE_CREATETHREAD, HAVE_CREATEMUTEX, and HAVE__BEGINTHREADEX - */ -#define HAVE_PTHREADS - -/* - * Do we have the futex syscall? - */ - -#define HAVE_FUTEX - -/* - * Process creation model. Choose one: - * - * HAVE_FORKEXEC - use fork() and exec() - * HAVE_WIN32_PROC - use CreateProcess() - */ -#define HAVE_FORKEXEC - -/* - * Process out-of-memory adjustment. Set if running on Linux, - * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory - * badness adjustment. - */ -#define HAVE_OOM_ADJ - -/* - * IPC model. Choose one: - * - * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget). - * HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap). - * HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping). - * HAVE_ANDROID_IPC - use Android versions (?, mmap). - */ -#define HAVE_SYSV_IPC - -/* - * Memory-mapping model. Choose one: - * - * HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h - * HAVE_WIN32_FILEMAP - use Win32 filemaps - */ -#define HAVE_POSIX_FILEMAP - -/* - * Define this if you have <termio.h> - */ -#define HAVE_TERMIO_H 1 - -/* - * Define this if you have <sys/sendfile.h> - */ -#define HAVE_SYS_SENDFILE_H 1 - -/* - * Define this if you build against MSVCRT.DLL - */ -/* #define HAVE_MS_C_RUNTIME */ - -/* - * Define this if you have sys/uio.h - */ -#define HAVE_SYS_UIO_H 1 - -/* - * Define this if your platforms implements symbolic links - * in its filesystems - */ -#define HAVE_SYMLINKS - -/* - * Define this if we have localtime_r(). - */ -#define HAVE_LOCALTIME_R 1 - -/* - * Define this if we have gethostbyname_r(). - */ -#define HAVE_GETHOSTBYNAME_R - -/* - * Define this if we have ioctl(). - */ -#define HAVE_IOCTL - -/* - * Define this if we want to use WinSock. - */ -/* #define HAVE_WINSOCK */ - -/* - * Define this if have clock_gettime() and friends - * - * Desktop Linux has this in librt, but it's broken in goobuntu, yielding - * mildly or wildly inaccurate results. - */ -/*#define HAVE_POSIX_CLOCKS*/ - -/* - * Define this if we have pthread_cond_timedwait_monotonic() and - * clock_gettime(CLOCK_MONOTONIC). - */ -/* #define HAVE_TIMEDWAIT_MONOTONIC */ - -/* - * Define this if we have linux style epoll() - */ -#define HAVE_EPOLL - -/* - * Endianness of the target machine. Choose one: - * - * HAVE_ENDIAN_H -- have endian.h header we can include. - * HAVE_LITTLE_ENDIAN -- we are little endian. - * HAVE_BIG_ENDIAN -- we are big endian. - */ -#define HAVE_ENDIAN_H -#define HAVE_LITTLE_ENDIAN - -/* - * We need to choose between 32-bit and 64-bit off_t. All of our code should - * agree on the same size. For desktop systems, use 64-bit values, - * because some of our libraries (e.g. wxWidgets) expect to be built that way. - */ -#define _FILE_OFFSET_BITS 64 -#define _LARGEFILE_SOURCE 1 - -/* - * Define if platform has off64_t (and lseek64 and other xxx64 functions) - */ -#define HAVE_OFF64_T - -/* - * Defined if we have the backtrace() call for retrieving a stack trace. - * Needed for CallStack to operate; if not defined, CallStack is - * non-functional. - */ -#define HAVE_BACKTRACE 1 - -/* - * Defined if we have the dladdr() call for retrieving the symbol associated - * with a memory address. If not defined, stack crawls will not have symbolic - * information. - */ -#define HAVE_DLADDR 1 - -/* - * Defined if we have the cxxabi.h header for demangling C++ symbols. If - * not defined, stack crawls will be displayed with raw mangled symbols - */ -#define HAVE_CXXABI 0 - -/* - * Defined if we have the gettid() system call. - */ -/* #define HAVE_GETTID */ - -/* - * Defined if we have the sched_setscheduler() call - */ -#define HAVE_SCHED_SETSCHEDULER - -/* - * Add any extra platform-specific defines here. - */ - -/* - * Define if we have <malloc.h> header - */ -#define HAVE_MALLOC_H - -/* - * Define if we have Linux-style non-filesystem Unix Domain Sockets - */ - -/* - * What CPU architecture does this platform use? - */ -#define ARCH_X86 - - -/* - * Define if we have Linux's inotify in <sys/inotify.h>. - */ -/*#define HAVE_INOTIFY 1*/ - -/* - * Define if we have madvise() in <sys/mman.h> - */ -#define HAVE_MADVISE 1 - -/* - * Define if tm struct has tm_gmtoff field - */ -#define HAVE_TM_GMTOFF 1 - -/* - * Define if dirent struct has d_type field - */ -#define HAVE_DIRENT_D_TYPE 1 - -/* - * Define if libc includes Android system properties implementation. - */ -/* #define HAVE_LIBC_SYSTEM_PROPERTIES */ - -/* - * Define if system provides a system property server (should be - * mutually exclusive with HAVE_LIBC_SYSTEM_PROPERTIES). - */ -#define HAVE_SYSTEM_PROPERTY_SERVER - -/* - * sprintf() format string for shared library naming. - */ -#define OS_SHARED_LIB_FORMAT_STR "lib%s.so" - -/* - * type for the third argument to mincore(). - */ -#define MINCORE_POINTER_TYPE unsigned char * - -/* - * Do we have the sigaction flag SA_NOCLDWAIT? - */ -#define HAVE_SA_NOCLDWAIT - -/* - * The default path separator for the platform - */ -#define OS_PATH_SEPARATOR '/' - -/* - * Is the filesystem case sensitive? - */ -#define OS_CASE_SENSITIVE - -/* - * Define if <sys/socket.h> exists. - */ -#define HAVE_SYS_SOCKET_H 1 - -/* - * Define if the strlcpy() function exists on the system. - */ -/* #define HAVE_STRLCPY 1 */ - -/* - * Define if the open_memstream() function exists on the system. - */ -#define HAVE_OPEN_MEMSTREAM 1 - -/* - * Define if the BSD funopen() function exists on the system. - */ -/* #define HAVE_FUNOPEN 1 */ - -/* - * Define if prctl() exists - */ -#define HAVE_PRCTL 1 - -/* - * Define if writev() exists - */ -#define HAVE_WRITEV 1 - -/* - * Define if <stdint.h> exists. - */ -#define HAVE_STDINT_H 1 - -/* - * Define if <stdbool.h> exists. - */ -#define HAVE_STDBOOL_H 1 - -/* - * Define if <sched.h> exists. - */ -#define HAVE_SCHED_H 1 - -/* - * Define if pread() exists - */ -#define HAVE_PREAD 1 - -/* - * Define if we have st_mtim in struct stat - */ -#define HAVE_STAT_ST_MTIM 1 - -/* - * Define if printf() supports %zd for size_t arguments - */ -#define HAVE_PRINTF_ZD 1 - -/* - * Define to 1 if <stdlib.h> provides qsort_r() with a BSD style function prototype. - */ -#define HAVE_BSD_QSORT_R 0 - -/* - * Define to 1 if <stdlib.h> provides qsort_r() with a GNU style function prototype. - */ -#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8) -#define HAVE_GNU_QSORT_R 1 -#else -#define HAVE_GNU_QSORT_R 0 -#endif - -#endif /*_ANDROID_CONFIG_H*/ diff --git a/include/arch/target_linux-x86/AndroidConfig.h b/include/arch/target_linux-x86/AndroidConfig.h deleted file mode 100644 index ab53892..0000000 --- a/include/arch/target_linux-x86/AndroidConfig.h +++ /dev/null @@ -1,363 +0,0 @@ -/* - * Copyright 2005 The Android Open Source Project - * - * Android config -- "target_linux-x86". Used for x86 linux target devices. - */ -#ifndef _ANDROID_CONFIG_H -#define _ANDROID_CONFIG_H - -/* - * =========================================================================== - * !!! IMPORTANT !!! - * =========================================================================== - * - * This file is included by ALL C/C++ source files. Don't put anything in - * here unless you are absolutely certain it can't go anywhere else. - * - * Any C++ stuff must be wrapped with "#ifdef __cplusplus". Do not use "//" - * comments. - */ - -/* - * Threading model. Choose one: - * - * HAVE_PTHREADS - use the pthreads library. - * HAVE_WIN32_THREADS - use Win32 thread primitives. - * -- combine HAVE_CREATETHREAD, HAVE_CREATEMUTEX, and HAVE__BEGINTHREADEX - */ -#define HAVE_PTHREADS - -/* - * Do we have pthread_setname_np()? - * - * (HAVE_PTHREAD_SETNAME_NP is used by WebKit to enable a function with - * the same name but different parameters, so we can't use that here.) - */ -#define HAVE_ANDROID_PTHREAD_SETNAME_NP - -/* - * Do we have the futex syscall? - */ -#define HAVE_FUTEX - -/* - * Define if we already have the futex wrapper functions defined. Yes if - * compiling against bionic. - */ -#define HAVE_FUTEX_WRAPPERS 1 - -/* - * Process creation model. Choose one: - * - * HAVE_FORKEXEC - use fork() and exec() - * HAVE_WIN32_PROC - use CreateProcess() - */ -#define HAVE_FORKEXEC - -/* - * Process out-of-memory adjustment. Set if running on Linux, - * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory - * badness adjustment. - */ -#define HAVE_OOM_ADJ - -/* - * IPC model. Choose one: - * - * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget). - * HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap). - * HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping). - * HAVE_ANDROID_IPC - use Android versions (?, mmap). - */ -#define HAVE_ANDROID_IPC 1 - -/* - * Memory-mapping model. Choose one: - * - * HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h - * HAVE_WIN32_FILEMAP - use Win32 filemaps - */ -#define HAVE_POSIX_FILEMAP 1 - -/* - * Define this if you have <termio.h> - */ -#define HAVE_TERMIO_H 1 - -/* - * Define this if you have <sys/sendfile.h> - */ -#define HAVE_SYS_SENDFILE_H 1 - -/* - * Define this if you build against have Microsoft C runtime (MSVCRT.DLL) - */ -/* #define HAVE_MS_C_RUNTIME */ - -/* - * Define this if you have sys/uio.h - */ -#define HAVE_SYS_UIO_H 1 - -/* - * Define this if your platforms implements symbolic links - * in its filesystems - */ -#define HAVE_SYMLINKS 1 - -/* - * Define this if we have localtime_r(). - */ -/* #define HAVE_LOCALTIME_R 1 */ - -/* - * Define this if we have gethostbyname_r(). - */ -/* #define HAVE_GETHOSTBYNAME_R */ - -/* - * Define this if we have ioctl(). - */ -#define HAVE_IOCTL - -/* - * Define this if we want to use WinSock. - */ -/* #define HAVE_WINSOCK */ - -/* - * Define this if have clock_gettime() and friends - * - */ -#define HAVE_POSIX_CLOCKS - -/* - * Define this if we have pthread_cond_timedwait_monotonic() and - * clock_gettime(CLOCK_MONOTONIC). - */ -#define HAVE_TIMEDWAIT_MONOTONIC - -/* - * Define this if we have linux style epoll() - */ -#define HAVE_EPOLL - -/* - * Endianness of the target machine. Choose one: - * - * HAVE_ENDIAN_H -- have endian.h header we can include. - * HAVE_LITTLE_ENDIAN -- we are little endian. - * HAVE_BIG_ENDIAN -- we are big endian. - */ -#define HAVE_ENDIAN_H -#define HAVE_LITTLE_ENDIAN - -/* - * We need to choose between 32-bit and 64-bit off_t. All of our code should - * agree on the same size. For desktop systems, use 64-bit values, - * because some of our libraries (e.g. wxWidgets) expect to be built that way. - */ -/* - * #define _FILE_OFFSET_BITS 64 - * #define _LARGEFILE_SOURCE 1 - */ - -/* - * Define if platform has off64_t (and lseek64 and other xxx64 functions) - */ -#define HAVE_OFF64_T - -/* - * Defined if we have the backtrace() call for retrieving a stack trace. - * Needed for CallStack to operate; if not defined, CallStack is - * non-functional. - */ -#define HAVE_BACKTRACE 0 - -/* - * Defined if we have the dladdr() call for retrieving the symbol associated - * with a memory address. If not defined, stack crawls will not have symbolic - * information. - */ -#define HAVE_DLADDR 0 - -/* - * Defined if we have the cxxabi.h header for demangling C++ symbols. If - * not defined, stack crawls will be displayed with raw mangled symbols - */ -#define HAVE_CXXABI 0 - -/* - * Defined if we have the gettid() system call. - */ -#define HAVE_GETTID - -/* - * Defined if we have the sched_setscheduler() call - */ -#define HAVE_SCHED_SETSCHEDULER - -/* - * Add any extra platform-specific defines here. - */ -#ifndef __linux__ -#define __linux__ -#endif - -/* - * Define if we have <malloc.h> header - */ -#define HAVE_MALLOC_H - -/* - * Define if we're running on *our* linux on device or emulator. - */ -#define HAVE_ANDROID_OS 1 - -/* - * Define if we have Linux-style non-filesystem Unix Domain Sockets - */ -#define HAVE_LINUX_LOCAL_SOCKET_NAMESPACE 1 - -/* - * Define if we have Linux's inotify in <sys/inotify.h>. - */ -#define HAVE_INOTIFY 1 - -/* - * Define if we have madvise() in <sys/mman.h> - */ -#define HAVE_MADVISE 1 - -/* - * Define if we have Linux's dbus - */ -/* #define HAVE_DBUS 1 */ - -/* - * Define if tm struct has tm_gmtoff field - */ -#define HAVE_TM_GMTOFF 1 - -/* - * Define if dirent struct has d_type field - */ -#define HAVE_DIRENT_D_TYPE 1 - -/* - * Define if libc includes Android system properties implementation. - */ -#define HAVE_LIBC_SYSTEM_PROPERTIES 1 - -/* - * Define if system provides a system property server (should be - * mutually exclusive with HAVE_LIBC_SYSTEM_PROPERTIES). - */ -/* #define HAVE_SYSTEM_PROPERTY_SERVER */ - -/* - * What CPU architecture does this platform use? - */ -#define ARCH_X86 - -/* - * sprintf() format string for shared library naming. - */ -#define OS_SHARED_LIB_FORMAT_STR "lib%s.so" - -/* - * Do we have __memcmp16()? - */ -/* #define HAVE__MEMCMP16 1 */ - -/* - * type for the third argument to mincore(). - */ -#define MINCORE_POINTER_TYPE unsigned char * - -/* - * Do we have the sigaction flag SA_NOCLDWAIT? - */ -#define HAVE_SA_NOCLDWAIT - -/* - * The default path separator for the platform - */ -#define OS_PATH_SEPARATOR '/' - -/* - * Is the filesystem case sensitive? - */ -#define OS_CASE_SENSITIVE - -/* - * Define if <sys/socket.h> exists. - */ -#define HAVE_SYS_SOCKET_H 1 - -/* - * Define if the strlcpy() function exists on the system. - */ -#define HAVE_STRLCPY 1 - -/* - * Define if the open_memstream() function exists on the system. - */ -/* #define HAVE_OPEN_MEMSTREAM 1 */ - -/* - * Define if the BSD funopen() function exists on the system. - */ -#define HAVE_FUNOPEN 1 - -/* - * Define if prctl() exists - */ -#define HAVE_PRCTL 1 - -/* - * Whether or not _Unwind_Context is defined as a struct. - */ -#define HAVE_UNWIND_CONTEXT_STRUCT - -/* - * Define if <stdint.h> exists. - */ -#define HAVE_STDINT_H 1 - -/* - * Define if <stdbool.h> exists. - */ -#define HAVE_STDBOOL_H 1 - -/* - * Define if <sched.h> exists. - */ -#define HAVE_SCHED_H 1 - -/* - * Define if pread() exists - */ -#define HAVE_PREAD 1 - -/* - * Define if we have st_mtim in struct stat - */ -#define HAVE_STAT_ST_MTIM 1 - -/* - * Define if printf() supports %zd for size_t arguments - */ -#define HAVE_PRINTF_ZD 1 - -/* - * Define to 1 if <stdlib.h> provides qsort_r() with a BSD style function prototype. - */ -#define HAVE_BSD_QSORT_R 0 - -/* - * Define to 1 if <stdlib.h> provides qsort_r() with a GNU style function prototype. - */ -#define HAVE_GNU_QSORT_R 0 - -#endif /* _ANDROID_CONFIG_H */ diff --git a/include/arch/windows/AndroidConfig.h b/include/arch/windows/AndroidConfig.h deleted file mode 100644 index 0274da5..0000000 --- a/include/arch/windows/AndroidConfig.h +++ /dev/null @@ -1,351 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -/* - * Android config -- "CYGWIN_NT-5.1". - * - * Cygwin has pthreads, but GDB seems to get confused if you use it to - * create threads. By "confused", I mean it freezes up the first time the - * debugged process creates a thread, even if you use CreateThread. The - * mere presence of pthreads linkage seems to cause problems. - */ -#ifndef _ANDROID_CONFIG_H -#define _ANDROID_CONFIG_H - -/* - * =========================================================================== - * !!! IMPORTANT !!! - * =========================================================================== - * - * This file is included by ALL C/C++ source files. Don't put anything in - * here unless you are absolutely certain it can't go anywhere else. - * - * Any C++ stuff must be wrapped with "#ifdef __cplusplus". Do not use "//" - * comments. - */ - -/* - * Threading model. Choose one: - * - * HAVE_PTHREADS - use the pthreads library. - * HAVE_WIN32_THREADS - use Win32 thread primitives. - */ -#define HAVE_WIN32_THREADS - -/* - * Do we have the futex syscall? - */ - -/* #define HAVE_FUTEX */ - - -/* - * Process creation model. Choose one: - * - * HAVE_FORKEXEC - use fork() and exec() - * HAVE_WIN32_PROC - use CreateProcess() - */ -#ifdef __CYGWIN__ -# define HAVE_FORKEXEC -#else -# define HAVE_WIN32_PROC -#endif - -/* - * Process out-of-memory adjustment. Set if running on Linux, - * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory - * badness adjustment. - */ -/* #define HAVE_OOM_ADJ */ - -/* - * IPC model. Choose one: - * - * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget). - * HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap). - * HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping). - * HAVE_ANDROID_IPC - use Android versions (?, mmap). - */ -#define HAVE_WIN32_IPC - -/* - * Memory-mapping model. Choose one: - * - * HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h - * HAVE_WIN32_FILEMAP - use Win32 filemaps - */ -#ifdef __CYGWIN__ -#define HAVE_POSIX_FILEMAP -#else -#define HAVE_WIN32_FILEMAP -#endif - -/* - * Define this if you have <termio.h> - */ -#ifdef __CYGWIN__ -# define HAVE_TERMIO_H -#endif - -/* - * Define this if you have <sys/sendfile.h> - */ -#ifdef __CYGWIN__ -# define HAVE_SYS_SENDFILE_H 1 -#endif - -/* - * Define this if you build against MSVCRT.DLL - */ -#ifndef __CYGWIN__ -# define HAVE_MS_C_RUNTIME -#endif - -/* - * Define this if you have sys/uio.h - */ -#ifdef __CYGWIN__ -#define HAVE_SYS_UIO_H -#endif - - -/* - * Define this if we have localtime_r(). - */ -/* #define HAVE_LOCALTIME_R 1 */ - -/* - * Define this if we have gethostbyname_r(). - */ -/* #define HAVE_GETHOSTBYNAME_R */ - -/* - * Define this if we have ioctl(). - */ -/* #define HAVE_IOCTL */ - -/* - * Define this if we want to use WinSock. - */ -#ifndef __CYGWIN__ -#define HAVE_WINSOCK -#endif - -/* - * Define this if your platforms implements symbolic links - * in its filesystems - */ -/* #define HAVE_SYMLINKS */ - -/* - * Define this if have clock_gettime() and friends - */ -/* #define HAVE_POSIX_CLOCKS */ - -/* - * Endianness of the target machine. Choose one: - * - * HAVE_ENDIAN_H -- have endian.h header we can include. - * HAVE_LITTLE_ENDIAN -- we are little endian. - * HAVE_BIG_ENDIAN -- we are big endian. - */ -#ifdef __CYGWIN__ -#define HAVE_ENDIAN_H -#endif - -#define HAVE_LITTLE_ENDIAN - -/* - * We need to choose between 32-bit and 64-bit off_t. All of our code should - * agree on the same size. For desktop systems, use 64-bit values, - * because some of our libraries (e.g. wxWidgets) expect to be built that way. - */ -#define _FILE_OFFSET_BITS 64 -#define _LARGEFILE_SOURCE 1 - -/* - * Define if platform has off64_t (and lseek64 and other xxx64 functions) - */ -#define HAVE_OFF64_T - -/* - * Defined if we have the backtrace() call for retrieving a stack trace. - * Needed for CallStack to operate; if not defined, CallStack is - * non-functional. - */ -#define HAVE_BACKTRACE 0 - -/* - * Defined if we have the dladdr() call for retrieving the symbol associated - * with a memory address. If not defined, stack crawls will not have symbolic - * information. - */ -#define HAVE_DLADDR 0 - -/* - * Defined if we have the cxxabi.h header for demangling C++ symbols. If - * not defined, stack crawls will be displayed with raw mangled symbols - */ -#define HAVE_CXXABI 0 - -/* - * Define if tm struct has tm_gmtoff field - */ -/* #define HAVE_TM_GMTOFF 1 */ - -/* - * Define if dirent struct has d_type field - */ -/* #define HAVE_DIRENT_D_TYPE 1 */ - -/* - * Define if libc includes Android system properties implementation. - */ -/* #define HAVE_LIBC_SYSTEM_PROPERTIES */ - -/* - * Define if system provides a system property server (should be - * mutually exclusive with HAVE_LIBC_SYSTEM_PROPERTIES). - */ -/* #define HAVE_SYSTEM_PROPERTY_SERVER */ - -/* - * Define if we have madvise() in <sys/mman.h> - */ -/*#define HAVE_MADVISE 1*/ - -/* - * Add any extra platform-specific defines here. - */ -#define WIN32 1 /* stock Cygwin doesn't define these */ -#define _WIN32 1 -#define _WIN32_WINNT 0x0500 /* admit to using >= Win2K */ - -#define HAVE_WINDOWS_PATHS /* needed by simulator */ - -/* - * What CPU architecture does this platform use? - */ -#define ARCH_X86 - -/* - * sprintf() format string for shared library naming. - */ -#define OS_SHARED_LIB_FORMAT_STR "lib%s.dll" - -/* - * type for the third argument to mincore(). - */ -#define MINCORE_POINTER_TYPE unsigned char * - -/* - * The default path separator for the platform - */ -#define OS_PATH_SEPARATOR '\\' - -/* - * Is the filesystem case sensitive? - */ -/* #define OS_CASE_SENSITIVE */ - -/* - * Define if <sys/socket.h> exists. - * Cygwin has it, but not MinGW. - */ -#ifdef USE_MINGW -/* #define HAVE_SYS_SOCKET_H */ -#else -#define HAVE_SYS_SOCKET_H 1 -#endif - -/* - * Define if the strlcpy() function exists on the system. - */ -/* #define HAVE_STRLCPY 1 */ - -/* - * Define if the open_memstream() function exists on the system. - */ -/* #define HAVE_OPEN_MEMSTREAM 1 */ - -/* - * Define if the BSD funopen() function exists on the system. - */ -/* #define HAVE_FUNOPEN 1 */ - -/* - * Define if <winsock2.h> exists. - * Only MinGW has it. - */ -#ifdef USE_MINGW -#define HAVE_WINSOCK2_H 1 -#else -/* #define HAVE_WINSOCK2_H */ -#endif - -/* - * Various definitions missing in MinGW - */ -#ifdef USE_MINGW -#define S_IRGRP 0 -#endif - -/* - * Define if writev() exists. - */ -/* #define HAVE_WRITEV */ - -/* - * Define if <stdint.h> exists. - */ -/* #define HAVE_STDINT_H */ - -/* - * Define if <stdbool.h> exists. - */ -#define HAVE_STDBOOL_H - -/* - * Define if <sched.h> exists. - */ -/* #define HAVE_SCHED_H */ - -/* - * Define if pread() exists - */ -/* #define HAVE_PREAD 1 */ - -/* - * Define if we have st_mtim in struct stat - */ -/* #define HAVE_STAT_ST_MTIM 1 */ - -/* - * Define if printf() supports %zd for size_t arguments - */ -/* #define HAVE_PRINTF_ZD 1 */ - -/* - * Define to 1 if <stdlib.h> provides qsort_r() with a BSD style function prototype. - */ -#define HAVE_BSD_QSORT_R 0 - -/* - * Define to 1 if <stdlib.h> provides qsort_r() with a GNU style function prototype. - */ -#define HAVE_GNU_QSORT_R 0 - -#endif /*_ANDROID_CONFIG_H*/ diff --git a/include/corkscrew/map_info.h b/include/corkscrew/map_info.h index c5cd8f8..c9b241d 100644 --- a/include/corkscrew/map_info.h +++ b/include/corkscrew/map_info.h @@ -21,6 +21,7 @@ #include <sys/types.h> #include <stdbool.h> +#include <stdint.h> #ifdef __cplusplus extern "C" { @@ -31,6 +32,7 @@ typedef struct map_info { uintptr_t start; uintptr_t end; bool is_readable; + bool is_writable; bool is_executable; void* data; // arbitrary data associated with the map by the user, initially NULL char name[]; @@ -45,9 +47,10 @@ void free_map_info_list(map_info_t* milist); /* Finds the memory map that contains the specified address. */ const map_info_t* find_map_info(const map_info_t* milist, uintptr_t addr); -/* Returns true if the addr is in an readable map. */ +/* Returns true if the addr is in a readable map. */ bool is_readable_map(const map_info_t* milist, uintptr_t addr); - +/* Returns true if the addr is in a writable map. */ +bool is_writable_map(const map_info_t* milist, uintptr_t addr); /* Returns true if the addr is in an executable map. */ bool is_executable_map(const map_info_t* milist, uintptr_t addr); diff --git a/include/corkscrew/ptrace.h b/include/corkscrew/ptrace.h index 9e0da78..76276d8 100644 --- a/include/corkscrew/ptrace.h +++ b/include/corkscrew/ptrace.h @@ -24,6 +24,7 @@ #include <sys/types.h> #include <stdbool.h> +#include <stdint.h> #ifdef __cplusplus extern "C" { diff --git a/include/corkscrew/symbol_table.h b/include/corkscrew/symbol_table.h index 020c8b8..4998750 100644 --- a/include/corkscrew/symbol_table.h +++ b/include/corkscrew/symbol_table.h @@ -17,6 +17,7 @@ #ifndef _CORKSCREW_SYMBOL_TABLE_H #define _CORKSCREW_SYMBOL_TABLE_H +#include <stdint.h> #include <sys/types.h> #ifdef __cplusplus diff --git a/include/cutils/atomic-arm.h b/include/cutils/atomic-arm.h index 16fe512..172a0cd 100644 --- a/include/cutils/atomic-arm.h +++ b/include/cutils/atomic-arm.h @@ -18,91 +18,75 @@ #define ANDROID_CUTILS_ATOMIC_ARM_H #include <stdint.h> -#include <machine/cpu-features.h> -extern inline void android_compiler_barrier(void) +#ifndef ANDROID_ATOMIC_INLINE +#define ANDROID_ATOMIC_INLINE inline __attribute__((always_inline)) +#endif + +extern ANDROID_ATOMIC_INLINE void android_compiler_barrier() { __asm__ __volatile__ ("" : : : "memory"); } -#if ANDROID_SMP == 0 -extern inline void android_memory_barrier(void) -{ - android_compiler_barrier(); -} -extern inline void android_memory_store_barrier(void) +extern ANDROID_ATOMIC_INLINE void android_memory_barrier() { +#if ANDROID_SMP == 0 android_compiler_barrier(); -} -#elif defined(__ARM_HAVE_DMB) -extern inline void android_memory_barrier(void) -{ +#else __asm__ __volatile__ ("dmb" : : : "memory"); +#endif } -extern inline void android_memory_store_barrier(void) -{ - __asm__ __volatile__ ("dmb st" : : : "memory"); -} -#elif defined(__ARM_HAVE_LDREX_STREX) -extern inline void android_memory_barrier(void) -{ - __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 5" : : "r" (0) : "memory"); -} -extern inline void android_memory_store_barrier(void) + +extern ANDROID_ATOMIC_INLINE void android_memory_store_barrier() { - android_memory_barrier(); -} +#if ANDROID_SMP == 0 + android_compiler_barrier(); #else -extern inline void android_memory_barrier(void) -{ - typedef void (kuser_memory_barrier)(void); - (*(kuser_memory_barrier *)0xffff0fa0)(); -} -extern inline void android_memory_store_barrier(void) -{ - android_memory_barrier(); -} + __asm__ __volatile__ ("dmb st" : : : "memory"); #endif +} -extern inline int32_t android_atomic_acquire_load(volatile const int32_t *ptr) +extern ANDROID_ATOMIC_INLINE +int32_t android_atomic_acquire_load(volatile const int32_t *ptr) { int32_t value = *ptr; android_memory_barrier(); return value; } -extern inline int32_t android_atomic_release_load(volatile const int32_t *ptr) +extern ANDROID_ATOMIC_INLINE +int32_t android_atomic_release_load(volatile const int32_t *ptr) { android_memory_barrier(); return *ptr; } -extern 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 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 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) @@ -110,47 +94,26 @@ extern inline int android_atomic_cas(int32_t old_value, int32_t new_value, } while (__builtin_expect(status != 0, 0)); return prev != old_value; } -#else -extern 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 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 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 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(); @@ -165,34 +128,19 @@ extern inline int32_t android_atomic_add(int32_t increment, } while (__builtin_expect(status != 0, 0)); return prev; } -#else -extern 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 inline int32_t android_atomic_inc(volatile int32_t *addr) +extern ANDROID_ATOMIC_INLINE int32_t android_atomic_inc(volatile int32_t *addr) { return android_atomic_add(1, addr); } -extern inline int32_t android_atomic_dec(volatile int32_t *addr) +extern ANDROID_ATOMIC_INLINE int32_t android_atomic_dec(volatile int32_t *addr) { 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 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(); @@ -207,23 +155,9 @@ extern inline int32_t android_atomic_and(int32_t value, volatile int32_t *ptr) } while (__builtin_expect(status != 0, 0)); return prev; } -#else -extern 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 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(); @@ -238,17 +172,5 @@ extern inline int32_t android_atomic_or(int32_t value, volatile int32_t *ptr) } while (__builtin_expect(status != 0, 0)); return prev; } -#else -extern 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/atomic-mips.h b/include/cutils/atomic-mips.h index 49144a3..f9d3e25 100644 --- a/include/cutils/atomic-mips.h +++ b/include/cutils/atomic-mips.h @@ -19,60 +19,66 @@ #include <stdint.h> -extern inline void android_compiler_barrier(void) +#ifndef ANDROID_ATOMIC_INLINE +#define ANDROID_ATOMIC_INLINE inline __attribute__((always_inline)) +#endif + +extern ANDROID_ATOMIC_INLINE void android_compiler_barrier(void) { __asm__ __volatile__ ("" : : : "memory"); } #if ANDROID_SMP == 0 -extern inline void android_memory_barrier(void) +extern ANDROID_ATOMIC_INLINE void android_memory_barrier(void) { android_compiler_barrier(); } -extern inline void android_memory_store_barrier(void) +extern ANDROID_ATOMIC_INLINE void android_memory_store_barrier(void) { android_compiler_barrier(); } #else -extern inline void android_memory_barrier(void) +extern ANDROID_ATOMIC_INLINE void android_memory_barrier(void) { __asm__ __volatile__ ("sync" : : : "memory"); } -extern inline void android_memory_store_barrier(void) +extern ANDROID_ATOMIC_INLINE void android_memory_store_barrier(void) { __asm__ __volatile__ ("sync" : : : "memory"); } #endif -extern inline int32_t android_atomic_acquire_load(volatile const int32_t *ptr) +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_acquire_load(volatile const int32_t *ptr) { int32_t value = *ptr; android_memory_barrier(); return value; } -extern inline int32_t android_atomic_release_load(volatile const int32_t *ptr) +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_release_load(volatile const int32_t *ptr) { android_memory_barrier(); return *ptr; } -extern 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 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; } -extern 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 { @@ -90,26 +96,28 @@ extern inline int android_atomic_cas(int32_t old_value, int32_t new_value, return prev != old_value; } -extern 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 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); } -extern inline int32_t android_atomic_swap(int32_t new_value, - volatile int32_t *ptr) +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_swap(int32_t new_value, volatile int32_t *ptr) { int32_t prev, status; do { @@ -125,8 +133,8 @@ extern inline int32_t android_atomic_swap(int32_t new_value, return prev; } -extern 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, status; android_memory_barrier(); @@ -142,17 +150,20 @@ extern inline int32_t android_atomic_add(int32_t increment, return prev; } -extern inline int32_t android_atomic_inc(volatile int32_t *addr) +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_inc(volatile int32_t *addr) { return android_atomic_add(1, addr); } -extern inline int32_t android_atomic_dec(volatile int32_t *addr) +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_dec(volatile int32_t *addr) { return android_atomic_add(-1, addr); } -extern 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, status; android_memory_barrier(); @@ -168,7 +179,8 @@ extern inline int32_t android_atomic_and(int32_t value, volatile int32_t *ptr) return prev; } -extern 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, status; android_memory_barrier(); diff --git a/include/cutils/atomic-x86.h b/include/cutils/atomic-x86.h index 438012e..9480f57 100644 --- a/include/cutils/atomic-x86.h +++ b/include/cutils/atomic-x86.h @@ -19,60 +19,66 @@ #include <stdint.h> -extern inline void android_compiler_barrier(void) +#ifndef ANDROID_ATOMIC_INLINE +#define ANDROID_ATOMIC_INLINE inline __attribute__((always_inline)) +#endif + +extern ANDROID_ATOMIC_INLINE void android_compiler_barrier(void) { __asm__ __volatile__ ("" : : : "memory"); } #if ANDROID_SMP == 0 -extern inline void android_memory_barrier(void) +extern ANDROID_ATOMIC_INLINE void android_memory_barrier(void) { android_compiler_barrier(); } -extern inline void android_memory_store_barrier(void) +extern ANDROID_ATOMIC_INLINE void android_memory_store_barrier(void) { android_compiler_barrier(); } #else -extern inline void android_memory_barrier(void) +extern ANDROID_ATOMIC_INLINE void android_memory_barrier(void) { __asm__ __volatile__ ("mfence" : : : "memory"); } -extern inline void android_memory_store_barrier(void) +extern ANDROID_ATOMIC_INLINE void android_memory_store_barrier(void) { android_compiler_barrier(); } #endif -extern inline int32_t android_atomic_acquire_load(volatile const int32_t *ptr) +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_acquire_load(volatile const int32_t *ptr) { int32_t value = *ptr; android_compiler_barrier(); return value; } -extern inline int32_t android_atomic_release_load(volatile const int32_t *ptr) +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_release_load(volatile const int32_t *ptr) { android_memory_barrier(); return *ptr; } -extern 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 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_compiler_barrier(); *ptr = value; } -extern 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; __asm__ __volatile__ ("lock; cmpxchgl %1, %2" @@ -82,24 +88,26 @@ extern inline int android_atomic_cas(int32_t old_value, int32_t new_value, return prev != old_value; } -extern 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) { /* Loads are not reordered with other loads. */ return android_atomic_cas(old_value, new_value, ptr); } -extern 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) { /* Stores are not reordered with other stores. */ return android_atomic_cas(old_value, new_value, ptr); } -extern 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) { __asm__ __volatile__ ("lock; xaddl %0, %1" : "+r" (increment), "+m" (*ptr) @@ -108,18 +116,20 @@ extern inline int32_t android_atomic_add(int32_t increment, return increment; } -extern inline int32_t android_atomic_inc(volatile int32_t *addr) +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_inc(volatile int32_t *addr) { return android_atomic_add(1, addr); } -extern inline int32_t android_atomic_dec(volatile int32_t *addr) +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_dec(volatile int32_t *addr) { return android_atomic_add(-1, addr); } -extern 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, status; do { @@ -129,7 +139,8 @@ extern inline int32_t android_atomic_and(int32_t value, return prev; } -extern 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, status; do { diff --git a/include/cutils/fs.h b/include/cutils/fs.h new file mode 100644 index 0000000..fd5296b --- /dev/null +++ b/include/cutils/fs.h @@ -0,0 +1,62 @@ +/* + * 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. + */ + +#ifndef __CUTILS_FS_H +#define __CUTILS_FS_H + +#include <sys/types.h> + +/* + * TEMP_FAILURE_RETRY is defined by some, but not all, versions of + * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's + * not already defined, then define it here. + */ +#ifndef TEMP_FAILURE_RETRY +/* Used to retry syscalls that can return EINTR. */ +#define TEMP_FAILURE_RETRY(exp) ({ \ + typeof (exp) _rc; \ + do { \ + _rc = (exp); \ + } while (_rc == -1 && errno == EINTR); \ + _rc; }) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Ensure that directory exists with given mode and owners. + */ +extern int fs_prepare_dir(const char* path, mode_t mode, uid_t uid, gid_t gid); + +/* + * Read single plaintext integer from given file, correctly handling files + * partially written with fs_write_atomic_int(). + */ +extern int fs_read_atomic_int(const char* path, int* value); + +/* + * Write single plaintext integer to given file, creating backup while + * in progress. + */ +extern int fs_write_atomic_int(const char* path, int value); + +#ifdef __cplusplus +} +#endif + +#endif /* __CUTILS_FS_H */ diff --git a/include/cutils/log.h b/include/cutils/log.h index 878952e..8b045c7 100644 --- a/include/cutils/log.h +++ b/include/cutils/log.h @@ -279,7 +279,88 @@ extern "C" { : (void)0 ) #endif - +// --------------------------------------------------------------------- + +/* + * Simplified macro to send a verbose radio log message using the current LOG_TAG. + */ +#ifndef RLOGV +#if LOG_NDEBUG +#define RLOGV(...) ((void)0) +#else +#define RLOGV(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) +#endif +#endif + +#define CONDITION(cond) (__builtin_expect((cond)!=0, 0)) + +#ifndef RLOGV_IF +#if LOG_NDEBUG +#define RLOGV_IF(cond, ...) ((void)0) +#else +#define RLOGV_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif +#endif + +/* + * Simplified macro to send a debug radio log message using the current LOG_TAG. + */ +#ifndef RLOGD +#define RLOGD(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef RLOGD_IF +#define RLOGD_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send an info radio log message using the current LOG_TAG. + */ +#ifndef RLOGI +#define RLOGI(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef RLOGI_IF +#define RLOGI_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send a warning radio log message using the current LOG_TAG. + */ +#ifndef RLOGW +#define RLOGW(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef RLOGW_IF +#define RLOGW_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send an error radio log message using the current LOG_TAG. + */ +#ifndef RLOGE +#define RLOGE(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef RLOGE_IF +#define RLOGE_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + // --------------------------------------------------------------------- diff --git a/include/cutils/multiuser.h b/include/cutils/multiuser.h new file mode 100644 index 0000000..635ddb1 --- /dev/null +++ b/include/cutils/multiuser.h @@ -0,0 +1,41 @@ +/* + * 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. + */ + +#ifndef __CUTILS_MULTIUSER_H +#define __CUTILS_MULTIUSER_H + +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +// NOTE: keep in sync with android.os.UserId + +#define MULTIUSER_APP_PER_USER_RANGE 100000 + +typedef uid_t userid_t; +typedef uid_t appid_t; + +extern userid_t multiuser_get_user_id(uid_t uid); +extern appid_t multiuser_get_app_id(uid_t uid); +extern uid_t multiuser_get_uid(userid_t userId, appid_t appId); + +#ifdef __cplusplus +} +#endif + +#endif /* __CUTILS_MULTIUSER_H */ 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/ion/ion.h b/include/ion/ion.h index cafead5..018c0a1 100644 --- a/include/ion/ion.h +++ b/include/ion/ion.h @@ -27,8 +27,11 @@ __BEGIN_DECLS int ion_open(); int ion_close(int fd); -int ion_alloc(int fd, size_t len, size_t align, unsigned int flags, - struct ion_handle **handle); +int ion_alloc(int fd, size_t len, size_t align, unsigned int heap_mask, + unsigned int flags, struct ion_handle **handle); +int ion_alloc_fd(int fd, size_t len, size_t align, unsigned int heap_mask, + unsigned int flags, int *handle_fd); +int ion_sync_fd(int fd, int handle_fd); int ion_free(int fd, struct ion_handle *handle); int ion_map(int fd, struct ion_handle *handle, size_t length, int prot, int flags, off_t offset, unsigned char **ptr, int *map_fd); diff --git a/include/mincrypt/rsa.h b/include/mincrypt/rsa.h index 7d7d571..d7429fc 100644 --- a/include/mincrypt/rsa.h +++ b/include/mincrypt/rsa.h @@ -13,14 +13,14 @@ ** be used to endorse or promote products derived from this software ** without specific prior written permission. ** -** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR +** THIS SOFTWARE IS PROVIDED BY Google 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 +** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO ** EVENT SHALL Google 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 +** 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. */ @@ -42,6 +42,7 @@ typedef struct RSAPublicKey { uint32_t n0inv; /* -1 / n[0] mod 2^32 */ uint32_t n[RSANUMWORDS]; /* modulus as little endian array */ uint32_t rr[RSANUMWORDS]; /* R^2 as little endian array */ + int exponent; /* 3 or 65537 */ } RSAPublicKey; int RSA_verify(const RSAPublicKey *key, diff --git a/include/netutils/ifc.h b/include/netutils/ifc.h index 67a4a45..1f5421d 100644 --- a/include/netutils/ifc.h +++ b/include/netutils/ifc.h @@ -34,6 +34,9 @@ extern int ifc_down(const char *name); extern int ifc_enable(const char *ifname); extern int ifc_disable(const char *ifname); +#define RESET_IPV4_ADDRESSES 0x01 +#define RESET_IPV6_ADDRESSES 0x02 +#define RESET_ALL_ADDRESSES (RESET_IPV4_ADDRESSES | RESET_IPV6_ADDRESSES) extern int ifc_reset_connections(const char *ifname, const int reset_mask); extern int ifc_get_addr(const char *name, in_addr_t *addr); @@ -66,6 +69,8 @@ extern int ifc_configure(const char *ifname, in_addr_t address, uint32_t prefixLength, in_addr_t gateway, in_addr_t dns1, in_addr_t dns2); +extern in_addr_t prefixLengthToIpv4Netmask(int prefix_length); + __END_DECLS #endif /* _NETUTILS_IFC_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 6521cbe..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. @@ -62,6 +69,8 @@ #define AID_DRMRPC 1026 /* group for drm rpc */ #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 */ @@ -76,6 +85,7 @@ #define AID_NET_ADMIN 3005 /* can configure interfaces and routing tables. */ #define AID_NET_BW_STATS 3006 /* read bandwidth statistics */ #define AID_NET_BW_ACCT 3007 /* change bandwidth statistics accounting */ +#define AID_NET_BT_STACK 3008 /* bluetooth: access config files */ #define AID_MISC 9998 /* access to misc storage */ #define AID_NOBODY 9999 @@ -87,6 +97,9 @@ #define AID_USER 100000 /* offset for uid ranges for each user */ +#define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */ +#define AID_SHARED_GID_END 59999 /* start of gids for apps in each user to share */ + #if !defined(EXCLUDE_FS_CONFIG_STRUCTURES) struct android_id_info { const char *name; @@ -119,6 +132,7 @@ static const struct android_id_info android_ids[] = { { "diag", AID_DIAG, }, { "net_bt_admin", AID_NET_BT_ADMIN, }, { "net_bt", AID_NET_BT, }, + { "net_bt_stack", AID_NET_BT_STACK, }, { "sdcard_r", AID_SDCARD_R, }, { "sdcard_rw", AID_SDCARD_RW, }, { "media_rw", AID_MEDIA_RW, }, @@ -132,8 +146,10 @@ static const struct android_id_info android_ids[] = { { "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, }, }; #define android_id_count \ @@ -143,6 +159,7 @@ struct fs_path_config { unsigned mode; unsigned uid; unsigned gid; + uint64_t capabilities; const char *prefix; }; @@ -152,26 +169,26 @@ struct fs_path_config { ** 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. @@ -180,61 +197,62 @@ static struct fs_path_config android_dirs[] = { ** 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" }, - { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/bluetooth/main.conf" }, - { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/bluetooth/input.conf" }, - { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/bluetooth/audio.conf" }, - { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/bluetooth/network.conf" }, - { 00444, AID_NET_BT, AID_NET_BT, "system/etc/bluetooth/blacklist.conf" }, - { 00640, AID_SYSTEM, AID_SYSTEM, "system/etc/bluetooth/auto_pairing.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] == '/') { + path++; + } + pc = dir ? android_dirs : android_files; plen = strlen(path); for(; pc->prefix; pc++){ @@ -254,6 +272,7 @@ static inline void fs_config(const char *path, int dir, *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/include/sync/sync.h b/include/sync/sync.h index 6aa5f2d..918acf6 100644 --- a/include/sync/sync.h +++ b/include/sync/sync.h @@ -19,13 +19,30 @@ #ifndef __SYS_CORE_SYNC_H #define __SYS_CORE_SYNC_H -#include <linux/sync.h> -#include <linux/sw_sync.h> +#include <sys/cdefs.h> +#include <stdint.h> __BEGIN_DECLS +// XXX: These structs are copied from the header "linux/sync.h". +struct sync_fence_info_data { + uint32_t len; + char name[32]; + int32_t status; + uint8_t pt_info[0]; +}; + +struct sync_pt_info { + uint32_t len; + char obj_name[32]; + char driver_name[32]; + int32_t status; + uint64_t timestamp_ns; + uint8_t driver_data[0]; +}; + /* timeout in msecs */ -int sync_wait(int fd, unsigned int timeout); +int sync_wait(int fd, int timeout); int sync_merge(const char *name, int fd1, int fd2); struct sync_fence_info_data *sync_fence_info(int fd); struct sync_pt_info *sync_pt_info(struct sync_fence_info_data *info, diff --git a/include/system/audio.h b/include/system/audio.h index 3807317..d246070 100644 --- a/include/system/audio.h +++ b/include/system/audio.h @@ -63,7 +63,10 @@ typedef enum { AUDIO_SOURCE_CAMCORDER = 5, AUDIO_SOURCE_VOICE_RECOGNITION = 6, AUDIO_SOURCE_VOICE_COMMUNICATION = 7, - + AUDIO_SOURCE_REMOTE_SUBMIX = 8, /* Source for the mix to be presented remotely. */ + /* An example of remote presentation is Wifi Display */ + /* where a dongle attached to a TV can be used to */ + /* play the mix captured by this audio source. */ AUDIO_SOURCE_CNT, AUDIO_SOURCE_MAX = AUDIO_SOURCE_CNT - 1, } audio_source_t; @@ -152,7 +155,7 @@ typedef enum { AUDIO_FORMAT_PCM_SUB_8_24_BIT), } audio_format_t; -typedef enum { +enum { /* output channels */ AUDIO_CHANNEL_OUT_FRONT_LEFT = 0x1, AUDIO_CHANNEL_OUT_FRONT_RIGHT = 0x2, @@ -275,7 +278,11 @@ typedef enum { AUDIO_IN_ACOUSTICS_TX_DISABLE = 0, } audio_in_acoustics_t; -typedef enum { +enum { + AUDIO_DEVICE_NONE = 0x0, + /* reserved bits */ + AUDIO_DEVICE_BIT_IN = 0x80000000, + AUDIO_DEVICE_BIT_DEFAULT = 0x40000000, /* output devices */ AUDIO_DEVICE_OUT_EARPIECE = 0x1, AUDIO_DEVICE_OUT_SPEAKER = 0x2, @@ -292,7 +299,8 @@ typedef enum { AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000, AUDIO_DEVICE_OUT_USB_ACCESSORY = 0x2000, AUDIO_DEVICE_OUT_USB_DEVICE = 0x4000, - AUDIO_DEVICE_OUT_DEFAULT = 0x8000, + AUDIO_DEVICE_OUT_REMOTE_SUBMIX = 0x8000, + AUDIO_DEVICE_OUT_DEFAULT = AUDIO_DEVICE_BIT_DEFAULT, AUDIO_DEVICE_OUT_ALL = (AUDIO_DEVICE_OUT_EARPIECE | AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_WIRED_HEADSET | @@ -308,6 +316,7 @@ typedef enum { AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET | AUDIO_DEVICE_OUT_USB_ACCESSORY | AUDIO_DEVICE_OUT_USB_DEVICE | + AUDIO_DEVICE_OUT_REMOTE_SUBMIX | AUDIO_DEVICE_OUT_DEFAULT), AUDIO_DEVICE_OUT_ALL_A2DP = (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | @@ -319,15 +328,20 @@ typedef enum { AUDIO_DEVICE_OUT_USB_DEVICE), /* input devices */ - AUDIO_DEVICE_IN_COMMUNICATION = 0x10000, - AUDIO_DEVICE_IN_AMBIENT = 0x20000, - AUDIO_DEVICE_IN_BUILTIN_MIC = 0x40000, - AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET = 0x80000, - AUDIO_DEVICE_IN_WIRED_HEADSET = 0x100000, - AUDIO_DEVICE_IN_AUX_DIGITAL = 0x200000, - AUDIO_DEVICE_IN_VOICE_CALL = 0x400000, - AUDIO_DEVICE_IN_BACK_MIC = 0x800000, - AUDIO_DEVICE_IN_DEFAULT = 0x80000000, + AUDIO_DEVICE_IN_COMMUNICATION = AUDIO_DEVICE_BIT_IN | 0x1, + AUDIO_DEVICE_IN_AMBIENT = AUDIO_DEVICE_BIT_IN | 0x2, + AUDIO_DEVICE_IN_BUILTIN_MIC = AUDIO_DEVICE_BIT_IN | 0x4, + AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET = AUDIO_DEVICE_BIT_IN | 0x8, + AUDIO_DEVICE_IN_WIRED_HEADSET = AUDIO_DEVICE_BIT_IN | 0x10, + AUDIO_DEVICE_IN_AUX_DIGITAL = AUDIO_DEVICE_BIT_IN | 0x20, + AUDIO_DEVICE_IN_VOICE_CALL = AUDIO_DEVICE_BIT_IN | 0x40, + AUDIO_DEVICE_IN_BACK_MIC = AUDIO_DEVICE_BIT_IN | 0x80, + AUDIO_DEVICE_IN_REMOTE_SUBMIX = AUDIO_DEVICE_BIT_IN | 0x100, + AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET = AUDIO_DEVICE_BIT_IN | 0x200, + AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET = AUDIO_DEVICE_BIT_IN | 0x400, + AUDIO_DEVICE_IN_USB_ACCESSORY = AUDIO_DEVICE_BIT_IN | 0x800, + AUDIO_DEVICE_IN_USB_DEVICE = AUDIO_DEVICE_BIT_IN | 0x1000, + AUDIO_DEVICE_IN_DEFAULT = AUDIO_DEVICE_BIT_IN | AUDIO_DEVICE_BIT_DEFAULT, AUDIO_DEVICE_IN_ALL = (AUDIO_DEVICE_IN_COMMUNICATION | AUDIO_DEVICE_IN_AMBIENT | @@ -337,9 +351,16 @@ typedef enum { AUDIO_DEVICE_IN_AUX_DIGITAL | AUDIO_DEVICE_IN_VOICE_CALL | AUDIO_DEVICE_IN_BACK_MIC | + AUDIO_DEVICE_IN_REMOTE_SUBMIX | + AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET | + AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET | + AUDIO_DEVICE_IN_USB_ACCESSORY | + AUDIO_DEVICE_IN_USB_DEVICE | AUDIO_DEVICE_IN_DEFAULT), AUDIO_DEVICE_IN_ALL_SCO = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, -} audio_devices_t; +}; + +typedef uint32_t audio_devices_t; /* the audio output flags serve two purposes: * - when an AudioTrack is created they indicate a "wish" to be connected to an @@ -366,7 +387,8 @@ typedef enum { static inline bool audio_is_output_device(audio_devices_t device) { - if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_OUT_ALL) == 0)) + if (((device & AUDIO_DEVICE_BIT_IN) == 0) && + (popcount(device) == 1) && ((device & ~AUDIO_DEVICE_OUT_ALL) == 0)) return true; else return false; @@ -374,12 +396,20 @@ static inline bool audio_is_output_device(audio_devices_t device) static inline bool audio_is_input_device(audio_devices_t device) { - if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_IN_ALL) == 0)) - return true; - else - return false; + if ((device & AUDIO_DEVICE_BIT_IN) != 0) { + device &= ~AUDIO_DEVICE_BIT_IN; + if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_IN_ALL) == 0)) + return true; + } + return false; } +static inline bool audio_is_output_devices(audio_devices_t device) +{ + return (device & AUDIO_DEVICE_BIT_IN) == 0; +} + + static inline bool audio_is_a2dp_device(audio_devices_t device) { if ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_ALL_A2DP)) @@ -390,6 +420,7 @@ static inline bool audio_is_a2dp_device(audio_devices_t device) static inline bool audio_is_bluetooth_sco_device(audio_devices_t device) { + device &= ~AUDIO_DEVICE_BIT_IN; if ((popcount(device) == 1) && (device & (AUDIO_DEVICE_OUT_ALL_SCO | AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET))) return true; @@ -405,6 +436,14 @@ static inline bool audio_is_usb_device(audio_devices_t device) return false; } +static inline bool audio_is_remote_submix_device(audio_devices_t device) +{ + if ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_REMOTE_SUBMIX)) + return true; + else + return false; +} + static inline bool audio_is_input_channel(uint32_t channel) { if ((channel & ~AUDIO_CHANNEL_IN_ALL) == 0) diff --git a/include/system/audio_policy.h b/include/system/audio_policy.h index 91b8e9c..a6554de 100644 --- a/include/system/audio_policy.h +++ b/include/system/audio_policy.h @@ -43,6 +43,7 @@ typedef enum { AUDIO_POLICY_FORCE_ANALOG_DOCK, AUDIO_POLICY_FORCE_DIGITAL_DOCK, AUDIO_POLICY_FORCE_NO_BT_A2DP, /* A2DP sink is not preferred to speaker or wired HS */ + AUDIO_POLICY_FORCE_SYSTEM_ENFORCED, AUDIO_POLICY_FORCE_CFG_CNT, AUDIO_POLICY_FORCE_CFG_MAX = AUDIO_POLICY_FORCE_CFG_CNT - 1, @@ -56,6 +57,7 @@ typedef enum { AUDIO_POLICY_FORCE_FOR_MEDIA, AUDIO_POLICY_FORCE_FOR_RECORD, AUDIO_POLICY_FORCE_FOR_DOCK, + AUDIO_POLICY_FORCE_FOR_SYSTEM, AUDIO_POLICY_FORCE_USE_CNT, AUDIO_POLICY_FORCE_USE_MAX = AUDIO_POLICY_FORCE_USE_CNT - 1, diff --git a/include/system/camera.h b/include/system/camera.h index e4cacc5..7a4dd53 100644 --- a/include/system/camera.h +++ b/include/system/camera.h @@ -163,6 +163,17 @@ enum { * can silently finish itself or show a dialog. */ CAMERA_CMD_PING = 9, + + /** + * Configure the number of video buffers used for recording. The intended + * video buffer count for recording is passed as arg1, which must be + * greater than 0. This command must be sent before recording is started. + * This command returns INVALID_OPERATION error if it is sent after video + * recording is started, or the command is not supported at all. This + * command also returns a BAD_VALUE error if the intended video buffer + * count is non-positive or too big to be realized. + */ + CAMERA_CMD_SET_VIDEO_BUFFER_COUNT = 10, }; /** camera fatal errors */ diff --git a/include/system/graphics.h b/include/system/graphics.h index 24e2bfb..82b5fcc 100644 --- a/include/system/graphics.h +++ b/include/system/graphics.h @@ -109,6 +109,37 @@ enum { */ HAL_PIXEL_FORMAT_RAW_SENSOR = 0x20, + /* + * Android binary blob graphics buffer format: + * + * This format is used to carry task-specific data which does not have a + * standard image structure. The details of the format are left to the two + * endpoints. + * + * A typical use case is for transporting JPEG-compressed images from the + * Camera HAL to the framework or to applications. + * + * Buffers of this format must have a height of 1, and width equal to their + * size in bytes. + */ + HAL_PIXEL_FORMAT_BLOB = 0x21, + + /* + * Android format indicating that the choice of format is entirely up to the + * device-specific Gralloc implementation. + * + * The Gralloc implementation should examine the usage bits passed in when + * allocating a buffer with this format, and it should derive the pixel + * format from those usage flags. This format will never be used with any + * of the GRALLOC_USAGE_SW_* usage flags. + * + * If a buffer of this format is to be used as an OpenGL ES texture, the + * framework will assume that sampling the texture will always return an + * alpha value of 1.0 (i.e. the buffer contains only opaque pixel values). + * + */ + HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22, + /* Legacy formats (deprecated), used by ImageFormat.java */ HAL_PIXEL_FORMAT_YCbCr_422_SP = 0x10, // NV16 HAL_PIXEL_FORMAT_YCrCb_420_SP = 0x11, // NV21 diff --git a/include/system/window.h b/include/system/window.h index 8e00bcd..4698fb3 100644 --- a/include/system/window.h +++ b/include/system/window.h @@ -17,11 +17,15 @@ #ifndef SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H #define SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H +#include <cutils/native_handle.h> +#include <errno.h> +#include <limits.h> #include <stdint.h> #include <string.h> +#include <sync/sync.h> #include <sys/cdefs.h> #include <system/graphics.h> -#include <cutils/native_handle.h> +#include <unistd.h> __BEGIN_DECLS @@ -38,6 +42,14 @@ __BEGIN_DECLS // --------------------------------------------------------------------------- +// This #define may be used to conditionally compile device-specific code to +// support either the prior ANativeWindow interface, which did not pass libsync +// fences around, or the new interface that does. This #define is only present +// when the ANativeWindow interface does include libsync support. +#define ANDROID_NATIVE_WINDOW_HAS_SYNC 1 + +// --------------------------------------------------------------------------- + typedef const native_handle_t* buffer_handle_t; // --------------------------------------------------------------------------- @@ -379,8 +391,12 @@ struct ANativeWindow * allowed if a specific buffer count has been set. * * Returns 0 on success or -errno on error. + * + * XXX: This function is deprecated. It will continue to work for some + * time for binary compatibility, but the new dequeueBuffer function that + * outputs a fence file descriptor should be used in its place. */ - int (*dequeueBuffer)(struct ANativeWindow* window, + int (*dequeueBuffer_DEPRECATED)(struct ANativeWindow* window, struct ANativeWindowBuffer** buffer); /* @@ -389,9 +405,14 @@ struct ANativeWindow * dequeueBuffer first. * * Returns 0 on success or -errno on error. + * + * XXX: This function is deprecated. It will continue to work for some + * time for binary compatibility, but it is essentially a no-op, and calls + * to it should be removed. */ - int (*lockBuffer)(struct ANativeWindow* window, + int (*lockBuffer_DEPRECATED)(struct ANativeWindow* window, struct ANativeWindowBuffer* buffer); + /* * Hook called by EGL when modifications to the render buffer are done. * This unlocks and post the buffer. @@ -405,8 +426,13 @@ struct ANativeWindow * Buffers MUST be queued in the same order than they were dequeued. * * Returns 0 on success or -errno on error. + * + * XXX: This function is deprecated. It will continue to work for some + * time for binary compatibility, but the new queueBuffer function that + * takes a fence file descriptor should be used in its place (pass a value + * of -1 for the fence file descriptor if there is no valid one to pass). */ - int (*queueBuffer)(struct ANativeWindow* window, + int (*queueBuffer_DEPRECATED)(struct ANativeWindow* window, struct ANativeWindowBuffer* buffer); /* @@ -463,12 +489,86 @@ struct ANativeWindow * reference if they might use the buffer after queueing or canceling it. * Holding a reference to a buffer after queueing or canceling it is only * allowed if a specific buffer count has been set. + * + * XXX: This function is deprecated. It will continue to work for some + * time for binary compatibility, but the new cancelBuffer function that + * takes a fence file descriptor should be used in its place (pass a value + * of -1 for the fence file descriptor if there is no valid one to pass). */ - int (*cancelBuffer)(struct ANativeWindow* window, + int (*cancelBuffer_DEPRECATED)(struct ANativeWindow* window, struct ANativeWindowBuffer* buffer); + /* + * Hook called by EGL to acquire a buffer. This call may block if no + * buffers are available. + * + * The window holds a reference to the buffer between dequeueBuffer and + * either queueBuffer or cancelBuffer, so clients only need their own + * reference if they might use the buffer after queueing or canceling it. + * Holding a reference to a buffer after queueing or canceling it is only + * allowed if a specific buffer count has been set. + * + * The libsync fence file descriptor returned in the int pointed to by the + * fenceFd argument will refer to the fence that must signal before the + * dequeued buffer may be written to. A value of -1 indicates that the + * caller may access the buffer immediately without waiting on a fence. If + * a valid file descriptor is returned (i.e. any value except -1) then the + * caller is responsible for closing the file descriptor. + * + * Returns 0 on success or -errno on error. + */ + int (*dequeueBuffer)(struct ANativeWindow* window, + struct ANativeWindowBuffer** buffer, int* fenceFd); - void* reserved_proc[2]; + /* + * Hook called by EGL when modifications to the render buffer are done. + * This unlocks and post the buffer. + * + * The window holds a reference to the buffer between dequeueBuffer and + * either queueBuffer or cancelBuffer, so clients only need their own + * reference if they might use the buffer after queueing or canceling it. + * Holding a reference to a buffer after queueing or canceling it is only + * allowed if a specific buffer count has been set. + * + * The fenceFd argument specifies a libsync fence file descriptor for a + * fence that must signal before the buffer can be accessed. If the buffer + * can be accessed immediately then a value of -1 should be used. The + * caller must not use the file descriptor after it is passed to + * queueBuffer, and the ANativeWindow implementation is responsible for + * closing it. + * + * Returns 0 on success or -errno on error. + */ + int (*queueBuffer)(struct ANativeWindow* window, + struct ANativeWindowBuffer* buffer, int fenceFd); + + /* + * Hook used to cancel a buffer that has been dequeued. + * No synchronization is performed between dequeue() and cancel(), so + * either external synchronization is needed, or these functions must be + * called from the same thread. + * + * The window holds a reference to the buffer between dequeueBuffer and + * either queueBuffer or cancelBuffer, so clients only need their own + * reference if they might use the buffer after queueing or canceling it. + * Holding a reference to a buffer after queueing or canceling it is only + * allowed if a specific buffer count has been set. + * + * The fenceFd argument specifies a libsync fence file decsriptor for a + * fence that must signal before the buffer can be accessed. If the buffer + * can be accessed immediately then a value of -1 should be used. + * + * Note that if the client has not waited on the fence that was returned + * from dequeueBuffer, that same fence should be passed to cancelBuffer to + * ensure that future uses of the buffer are preceded by a wait on that + * fence. The caller must not use the file descriptor after it is passed + * to cancelBuffer, and the ANativeWindow implementation is responsible for + * closing it. + * + * Returns 0 on success or -errno on error. + */ + int (*cancelBuffer)(struct ANativeWindow* window, + struct ANativeWindowBuffer* buffer, int fenceFd); }; /* Backwards compatibility: use ANativeWindow (struct ANativeWindow in C). @@ -551,7 +651,7 @@ static inline int native_window_set_post_transform_crop( /* * native_window_set_active_rect(..., active_rect) * - * This function is deprectated and will be removed soon. For now it simply + * This function is deprecated and will be removed soon. For now it simply * sets the post-transform crop for compatibility while multi-project commits * get checked. */ @@ -717,6 +817,17 @@ static inline int native_window_api_disconnect( return window->perform(window, NATIVE_WINDOW_API_DISCONNECT, api); } +/* + * native_window_dequeue_buffer_and_wait(...) + * Dequeue a buffer and wait on the fence associated with that buffer. The + * buffer may safely be accessed immediately upon this function returning. An + * error is returned if either of the dequeue or the wait operations fail. + */ +static inline int native_window_dequeue_buffer_and_wait(ANativeWindow *anw, + struct ANativeWindowBuffer** anb) { + return anw->dequeueBuffer_DEPRECATED(anw, anb); +} + __END_DECLS diff --git a/include/sysutils/FrameworkListener.h b/include/sysutils/FrameworkListener.h index 756bacf..f1a4b43 100644 --- a/include/sysutils/FrameworkListener.h +++ b/include/sysutils/FrameworkListener.h @@ -23,10 +23,11 @@ class SocketClient; class FrameworkListener : public SocketListener { public: - static const int CMD_ARGS_MAX = 16; + static const int CMD_ARGS_MAX = 26; /* 1 out of errorRate will be dropped */ int errorRate; + private: int mCommandCount; bool mWithSeq; diff --git a/include/usbhost/usbhost.h b/include/usbhost/usbhost.h index 9a6b59c..1d67c12 100644 --- a/include/usbhost/usbhost.h +++ b/include/usbhost/usbhost.h @@ -72,6 +72,19 @@ struct usb_host_context *usb_host_init(void); /* Call this to cleanup the USB host library. */ void usb_host_cleanup(struct usb_host_context *context); +/* Call this to get the inotify file descriptor. */ +int usb_host_get_fd(struct usb_host_context *context); + +/* Call this to initialize the usb host context. */ +int usb_host_load(struct usb_host_context *context, + usb_device_added_cb added_cb, + usb_device_removed_cb removed_cb, + usb_discovery_done_cb discovery_done_cb, + void *client_data); + +/* Call this to read and handle occuring usb event. */ +int usb_host_read_event(struct usb_host_context *context); + /* Call this to monitor the USB bus for new and removed devices. * This is intended to be called from a dedicated thread, * as it will not return until one of the callbacks returns true. diff --git a/init/Android.mk b/init/Android.mk index 7dae9df..00d2144 100644 --- a/init/Android.mk +++ b/init/Android.mk @@ -15,7 +15,8 @@ LOCAL_SRC_FILES:= \ signal_handler.c \ init_parser.c \ ueventd.c \ - ueventd_parser.c + ueventd_parser.c \ + watchdogd.c ifeq ($(strip $(INIT_BOOTCHART)),true) LOCAL_SRC_FILES += bootchart.c @@ -32,18 +33,19 @@ LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT) LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED) -LOCAL_STATIC_LIBRARIES := libfs_mgr libcutils libc - -ifeq ($(HAVE_SELINUX),true) -LOCAL_STATIC_LIBRARIES += libselinux -LOCAL_C_INCLUDES += external/libselinux/include -LOCAL_CFLAGS += -DHAVE_SELINUX -endif +LOCAL_STATIC_LIBRARIES := \ + libfs_mgr \ + libcutils \ + libc \ + libselinux include $(BUILD_EXECUTABLE) -# Make a symlink from /sbin/ueventd to /init -SYMLINKS := $(TARGET_ROOT_OUT)/sbin/ueventd +# Make a symlink from /sbin/ueventd and /sbin/watchdogd to /init +SYMLINKS := \ + $(TARGET_ROOT_OUT)/sbin/ueventd \ + $(TARGET_ROOT_OUT)/sbin/watchdogd + $(SYMLINKS): INIT_BINARY := $(LOCAL_MODULE) $(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk @echo "Symlink: $@ -> ../$(INIT_BINARY)" diff --git a/init/builtins.c b/init/builtins.c index 882ceb5..dc7900e 100644 --- a/init/builtins.c +++ b/init/builtins.c @@ -35,10 +35,8 @@ #include <sys/system_properties.h> #include <fs_mgr.h> -#ifdef HAVE_SELINUX #include <selinux/selinux.h> #include <selinux/label.h> -#endif #include "init.h" #include "keywords.h" @@ -322,6 +320,14 @@ int do_mkdir(int nargs, char **args) if (_chown(args[1], uid, gid) < 0) { return -errno; } + + /* chown may have cleared S_ISUID and S_ISGID, chmod again */ + if (mode & (S_ISUID | S_ISGID)) { + ret = _chmod(args[1], mode); + if (ret == -1) { + return -errno; + } + } } return 0; @@ -339,6 +345,12 @@ static struct { { "ro", MS_RDONLY }, { "rw", 0 }, { "remount", MS_REMOUNT }, + { "bind", MS_BIND }, + { "rec", MS_REC }, + { "unbindable", MS_UNBINDABLE }, + { "private", MS_PRIVATE }, + { "slave", MS_SLAVE }, + { "shared", MS_SHARED }, { "defaults", 0 }, { 0, 0 }, }; @@ -501,24 +513,20 @@ int do_mount_all(int nargs, char **args) } int do_setcon(int nargs, char **args) { -#ifdef HAVE_SELINUX if (is_selinux_enabled() <= 0) return 0; if (setcon(args[1]) < 0) { return -errno; } -#endif return 0; } int do_setenforce(int nargs, char **args) { -#ifdef HAVE_SELINUX if (is_selinux_enabled() <= 0) return 0; if (security_setenforce(atoi(args[1])) < 0) { return -errno; } -#endif return 0; } @@ -746,36 +754,30 @@ int do_restorecon(int nargs, char **args) { } int do_setsebool(int nargs, char **args) { -#ifdef HAVE_SELINUX - SELboolean *b = alloca(nargs * sizeof(SELboolean)); - char *v; - int i; + const char *name = args[1]; + const char *value = args[2]; + SELboolean b; + int ret; if (is_selinux_enabled() <= 0) return 0; - for (i = 1; i < nargs; i++) { - char *name = args[i]; - v = strchr(name, '='); - if (!v) { - ERROR("setsebool: argument %s had no =\n", name); - return -EINVAL; - } - *v++ = 0; - b[i-1].name = name; - if (!strcmp(v, "1") || !strcasecmp(v, "true") || !strcasecmp(v, "on")) - b[i-1].value = 1; - else if (!strcmp(v, "0") || !strcasecmp(v, "false") || !strcasecmp(v, "off")) - b[i-1].value = 0; - else { - ERROR("setsebool: invalid value %s\n", v); - return -EINVAL; - } + b.name = name; + if (!strcmp(value, "1") || !strcasecmp(value, "true") || !strcasecmp(value, "on")) + b.value = 1; + else if (!strcmp(value, "0") || !strcasecmp(value, "false") || !strcasecmp(value, "off")) + b.value = 0; + else { + ERROR("setsebool: invalid value %s\n", value); + return -EINVAL; + } + + if (security_set_boolean_list(1, &b, 0) < 0) { + ret = -errno; + ERROR("setsebool: could not set %s to %s\n", name, value); + return ret; } - if (security_set_boolean_list(nargs - 1, b, 0) < 0) - return -errno; -#endif return 0; } diff --git a/init/devices.c b/init/devices.c index e43dbaf..e25034c 100644 --- a/init/devices.c +++ b/init/devices.c @@ -30,11 +30,9 @@ #include <sys/un.h> #include <linux/netlink.h> -#ifdef HAVE_SELINUX #include <selinux/selinux.h> #include <selinux/label.h> #include <selinux/android.h> -#endif #include <private/android_filesystem_config.h> #include <sys/time.h> @@ -51,10 +49,9 @@ #define SYSFS_PREFIX "/sys" #define FIRMWARE_DIR1 "/etc/firmware" #define FIRMWARE_DIR2 "/vendor/firmware" +#define FIRMWARE_DIR3 "/firmware/image" -#ifdef HAVE_SELINUX extern struct selabel_handle *sehandle; -#endif static int device_fd = -1; @@ -86,7 +83,8 @@ struct perm_node { struct platform_node { char *name; - int name_len; + char *path; + int path_len; struct listnode list; }; @@ -192,17 +190,15 @@ static void make_device(const char *path, unsigned gid; mode_t mode; dev_t dev; -#ifdef HAVE_SELINUX char *secontext = NULL; -#endif mode = get_device_perm(path, &uid, &gid) | (block ? S_IFBLK : S_IFCHR); -#ifdef HAVE_SELINUX + if (sehandle) { selabel_lookup(sehandle, &secontext, path, mode); setfscreatecon(secontext); } -#endif + dev = makedev(major, minor); /* Temporarily change egid to avoid race condition setting the gid of the * device node. Unforunately changing the euid would prevent creation of @@ -213,69 +209,76 @@ static void make_device(const char *path, mknod(path, mode, dev); chown(path, uid, -1); setegid(AID_ROOT); -#ifdef HAVE_SELINUX + if (secontext) { freecon(secontext); setfscreatecon(NULL); } -#endif } -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; @@ -361,8 +364,10 @@ static char **get_character_device_symlinks(struct uevent *uevent) 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); @@ -371,7 +376,7 @@ static char **get_character_device_symlinks(struct uevent *uevent) 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; @@ -408,7 +413,7 @@ err: 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]; @@ -420,18 +425,16 @@ static char **parse_platform_block_device(struct uevent *uevent) 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); @@ -453,17 +456,13 @@ static char **parse_platform_block_device(struct uevent *uevent) 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, @@ -496,12 +495,12 @@ static void handle_device(const char *action, const char *devpath, 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) @@ -539,7 +538,7 @@ static void handle_block_device_event(struct uevent *uevent) 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, @@ -638,7 +637,7 @@ static void handle_generic_device_event(struct uevent *uevent) static void handle_device_event(struct uevent *uevent) { - if (!strcmp(uevent->action,"add")) + if (!strcmp(uevent->action,"add") || !strcmp(uevent->action, "change")) fixup_sys_perms(uevent->path); if (!strncmp(uevent->subsystem, "block", 5)) { @@ -703,7 +702,7 @@ static int is_booting(void) static void process_firmware_event(struct uevent *uevent) { - char *root, *loading, *data, *file1 = NULL, *file2 = NULL; + char *root, *loading, *data, *file1 = NULL, *file2 = NULL, *file3 = NULL; int l, loading_fd, data_fd, fw_fd; int booting = is_booting(); @@ -730,6 +729,10 @@ static void process_firmware_event(struct uevent *uevent) if (l == -1) goto data_free_out; + l = asprintf(&file3, FIRMWARE_DIR3"/%s", uevent->firmware); + if (l == -1) + goto data_free_out; + loading_fd = open(loading, O_WRONLY); if(loading_fd < 0) goto file_free_out; @@ -743,17 +746,20 @@ try_loading_again: if(fw_fd < 0) { fw_fd = open(file2, O_RDONLY); if (fw_fd < 0) { - if (booting) { - /* If we're not fully booted, we may be missing - * filesystems needed for firmware, wait and retry. - */ - usleep(100000); - booting = is_booting(); - goto try_loading_again; + fw_fd = open(file3, O_RDONLY); + if (fw_fd < 0) { + if (booting) { + /* If we're not fully booted, we may be missing + * filesystems needed for firmware, wait and retry. + */ + usleep(100000); + booting = is_booting(); + goto try_loading_again; + } + INFO("firmware: could not open '%s' %d\n", uevent->firmware, errno); + write(loading_fd, "-1", 2); + goto data_close_out; } - INFO("firmware: could not open '%s' %d\n", uevent->firmware, errno); - write(loading_fd, "-1", 2); - goto data_close_out; } } @@ -874,14 +880,14 @@ void device_init(void) suseconds_t t0, t1; struct stat info; int fd; -#ifdef HAVE_SELINUX + sehandle = NULL; if (is_selinux_enabled() > 0) { sehandle = selinux_android_file_context_handle(); } -#endif - /* 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 b2e39bd..b28b0ab 100755 --- a/init/init.c +++ b/init/init.c @@ -32,11 +32,9 @@ #include <sys/socket.h> #include <sys/un.h> -#ifdef HAVE_SELINUX #include <selinux/selinux.h> #include <selinux/label.h> #include <selinux/android.h> -#endif #include <libgen.h> @@ -58,11 +56,10 @@ #include "init_parser.h" #include "util.h" #include "ueventd.h" +#include "watchdogd.h" -#ifdef HAVE_SELINUX struct selabel_handle *sehandle; struct selabel_handle *sehandle_prop; -#endif static int property_triggers_enabled = 0; @@ -76,9 +73,7 @@ static char hardware[32]; static unsigned revision = 0; static char qemu[32]; -#ifdef HAVE_SELINUX static int selinux_enabled = 1; -#endif static struct action *cur_action = NULL; static struct command *cur_command = NULL; @@ -162,10 +157,9 @@ void service_start(struct service *svc, const char *dynamic_args) pid_t pid; int needs_console; int n; -#ifdef HAVE_SELINUX char *scon = NULL; int rc; -#endif + /* starting a service removes it from the disabled or reset * state and immediately takes it out of the restarting * state if it was in there @@ -202,33 +196,39 @@ void service_start(struct service *svc, const char *dynamic_args) return; } -#ifdef HAVE_SELINUX 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); - freecon(mycon); - return; - } + 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); - freecon(fcon); - if (rc < 0) { - ERROR("could not get context while starting '%s'\n", svc->name); - 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; + } } } -#endif NOTICE("starting '%s'\n", svc->name); @@ -250,9 +250,7 @@ void service_start(struct service *svc, const char *dynamic_args) for (ei = svc->envvars; ei; ei = ei->next) add_environment(ei->name, ei->value); -#ifdef HAVE_SELINUX setsockcreatecon(scon); -#endif for (si = svc->sockets; si; si = si->next) { int socket_type = ( @@ -265,11 +263,9 @@ void service_start(struct service *svc, const char *dynamic_args) } } -#ifdef HAVE_SELINUX freecon(scon); scon = NULL; setsockcreatecon(NULL); -#endif if (svc->ioprio_class != IoSchedClass_NONE) { if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) { @@ -315,15 +311,12 @@ void service_start(struct service *svc, const char *dynamic_args) _exit(127); } } - -#ifdef HAVE_SELINUX if (svc->seclabel) { if (is_selinux_enabled() > 0 && setexeccon(svc->seclabel) < 0) { ERROR("cannot setexeccon('%s'): %s\n", svc->seclabel, strerror(errno)); _exit(127); } } -#endif if (!dynamic_args) { if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) { @@ -350,9 +343,7 @@ void service_start(struct service *svc, const char *dynamic_args) _exit(127); } -#ifdef HAVE_SELINUX freecon(scon); -#endif if (pid < 0) { ERROR("failed to start '%s'\n", svc->name); @@ -603,11 +594,9 @@ static void import_kernel_nv(char *name, int for_emulator) *value++ = 0; if (name_len == 0) return; -#ifdef HAVE_SELINUX if (!strcmp(name,"selinux")) { selinux_enabled = atoi(value); } -#endif if (for_emulator) { /* in the emulator, export any kernel option with the @@ -755,9 +744,8 @@ static int bootchart_init_action(int nargs, char **args) } #endif -#ifdef HAVE_SELINUX 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 } }; @@ -814,8 +802,6 @@ int audit_callback(void *data, security_class_t cls, char *buf, size_t len) return 0; } -#endif - int main(int argc, char **argv) { int fd_count = 0; @@ -831,6 +817,9 @@ int main(int argc, char **argv) if (!strcmp(basename(argv[0]), "ueventd")) return ueventd_main(argc, argv); + if (!strcmp(basename(argv[0]), "watchdogd")) + return watchdogd_main(argc, argv); + /* clear the umask */ umask(0); @@ -866,7 +855,6 @@ int main(int argc, char **argv) process_kernel_cmdline(); -#ifdef HAVE_SELINUX union selinux_callback cb; cb.func_log = klog_write; selinux_set_callback(SELINUX_CB_LOG, cb); @@ -891,7 +879,6 @@ int main(int argc, char **argv) */ restorecon("/dev"); restorecon("/dev/socket"); -#endif is_charger = !strcmp(bootmode, "charger"); diff --git a/init/init.h b/init/init.h index b7e06c9..955e1f0 100644 --- a/init/init.h +++ b/init/init.h @@ -95,9 +95,7 @@ struct service { gid_t supp_gids[NR_SVC_SUPP_GIDS]; size_t nr_supp_gids; -#ifdef HAVE_SELINUX char *seclabel; -#endif struct socketinfo *sockets; struct svcenvinfo *envvars; @@ -136,10 +134,8 @@ void property_changed(const char *name, const char *value); int load_565rle_image( char *file_name ); -#ifdef HAVE_SELINUX extern struct selabel_handle *sehandle; extern struct selabel_handle *sehandle_prop; extern int selinux_reload_policy(void); -#endif #endif /* _INIT_INIT_H */ diff --git a/init/init_parser.c b/init/init_parser.c index 5393e52..beb9188 100644 --- a/init/init_parser.c +++ b/init/init_parser.c @@ -799,13 +799,11 @@ static void parse_line_service(struct parse_state *state, int nargs, char **args } break; case K_seclabel: -#ifdef HAVE_SELINUX if (nargs != 2) { parse_error(state, "seclabel option requires a label string\n"); } else { svc->seclabel = args[1]; } -#endif break; default: diff --git a/init/keywords.h b/init/keywords.h index 97d4950..f188db5 100644 --- a/init/keywords.h +++ b/init/keywords.h @@ -78,7 +78,7 @@ enum { KEYWORD(setkey, COMMAND, 0, do_setkey) KEYWORD(setprop, COMMAND, 2, do_setprop) KEYWORD(setrlimit, COMMAND, 3, do_setrlimit) - KEYWORD(setsebool, COMMAND, 1, do_setsebool) + KEYWORD(setsebool, COMMAND, 2, do_setsebool) KEYWORD(socket, OPTION, 0, 0) KEYWORD(start, COMMAND, 1, do_start) KEYWORD(stop, COMMAND, 1, do_stop) diff --git a/init/property_service.c b/init/property_service.c index 5017375..61dd86f 100644..100755 --- a/init/property_service.c +++ b/init/property_service.c @@ -40,10 +40,8 @@ #include <sys/atomics.h> #include <private/android_filesystem_config.h> -#ifdef HAVE_SELINUX #include <selinux/selinux.h> #include <selinux/label.h> -#endif #include "property_service.h" #include "init.h" @@ -81,6 +79,7 @@ struct { { "sys.", AID_SYSTEM, 0 }, { "service.", AID_SYSTEM, 0 }, { "wlan.", AID_SYSTEM, 0 }, + { "bluetooth.", AID_BLUETOOTH, 0 }, { "dhcp.", AID_SYSTEM, 0 }, { "dhcp.", AID_DHCP, 0 }, { "debug.", AID_SYSTEM, 0 }, @@ -91,6 +90,7 @@ struct { { "persist.sys.", AID_SYSTEM, 0 }, { "persist.service.", AID_SYSTEM, 0 }, { "persist.security.", AID_SYSTEM, 0 }, + { "persist.service.bdroid.", AID_BLUETOOTH, 0 }, { "selinux." , AID_SYSTEM, 0 }, { NULL, 0, 0 } }; @@ -123,7 +123,7 @@ static int init_workspace(workspace *w, size_t size) /* 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, 0600); + fd = open("/dev/__properties__", O_RDWR | O_CREAT | O_NOFOLLOW, 0600); if (fd < 0) return -1; @@ -136,7 +136,7 @@ static int init_workspace(workspace *w, size_t size) close(fd); - fd = open("/dev/__properties__", O_RDONLY); + fd = open("/dev/__properties__", O_RDONLY | O_NOFOLLOW); if (fd < 0) return -1; @@ -199,7 +199,6 @@ static void update_prop_info(prop_info *pi, const char *value, unsigned len) static int check_mac_perms(const char *name, char *sctx) { -#ifdef HAVE_SELINUX if (is_selinux_enabled() <= 0) return 1; @@ -223,15 +222,10 @@ static int check_mac_perms(const char *name, char *sctx) freecon(tctx); err: return result; - -#endif - return 1; } static int check_control_mac_perms(const char *name, char *sctx) { -#ifdef HAVE_SELINUX - /* * Create a name prefix out of ctl.<service name> * The new prefix allows the use of the existing @@ -245,9 +239,6 @@ static int check_control_mac_perms(const char *name, char *sctx) return 0; return check_mac_perms(ctl_name, sctx); - -#endif - return 1; } /* @@ -318,13 +309,12 @@ const char* property_get(const char *name) static void write_persistent_property(const char *name, const char *value) { - const char *tempPath = PERSISTENT_PROPERTY_DIR "/.temp"; + char tempPath[PATH_MAX]; char path[PATH_MAX]; - int fd, length; - - snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, name); + int fd; - fd = open(tempPath, O_WRONLY|O_CREAT|O_TRUNC, 0600); + snprintf(tempPath, sizeof(tempPath), "%s/.temp.XXXXXX", PERSISTENT_PROPERTY_DIR); + fd = mkstemp(tempPath); if (fd < 0) { ERROR("Unable to write persistent property to temp file %s errno: %d\n", tempPath, errno); return; @@ -332,6 +322,7 @@ static void write_persistent_property(const char *name, const char *value) write(fd, value, strlen(value)); close(fd); + snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, name); if (rename(tempPath, path)) { unlink(tempPath); ERROR("Unable to rename persistent property file %s to %s\n", tempPath, path); @@ -343,8 +334,8 @@ int property_set(const char *name, const char *value) prop_area *pa; prop_info *pi; - int namelen = strlen(name); - int valuelen = strlen(value); + size_t namelen = strlen(name); + size_t valuelen = strlen(value); if(namelen >= PROP_NAME_MAX) return -1; if(valuelen >= PROP_VALUE_MAX) return -1; @@ -394,11 +385,9 @@ int property_set(const char *name, const char *value) * to prevent them from being overwritten by default values. */ write_persistent_property(name, value); -#ifdef HAVE_SELINUX } else if (strcmp("selinux.reload_policy", name) == 0 && strcmp("1", value) == 0) { selinux_reload_policy(); -#endif } property_changed(name, value); return 0; @@ -423,13 +412,13 @@ void handle_property_set_fd() /* Check socket options here */ if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) { close(s); - ERROR("Unable to recieve socket options\n"); + ERROR("Unable to receive socket options\n"); return; } r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), 0)); if(r != sizeof(prop_msg)) { - ERROR("sys_prop: mis-match msg size recieved: %d expected: %d errno: %d\n", + ERROR("sys_prop: mis-match msg size received: %d expected: %d errno: %d\n", r, sizeof(prop_msg), errno); close(s); return; @@ -440,9 +429,7 @@ void handle_property_set_fd() msg.name[PROP_NAME_MAX-1] = 0; msg.value[PROP_VALUE_MAX-1] = 0; -#ifdef HAVE_SELINUX getpeercon(s, &source_ctx); -#endif if(memcmp(msg.name,"ctl.",4) == 0) { // Keep the old close-socket-early behavior when handling @@ -467,10 +454,7 @@ void handle_property_set_fd() // the property is written to memory. close(s); } -#ifdef HAVE_SELINUX freecon(source_ctx); -#endif - break; default: @@ -528,12 +512,14 @@ static void load_properties_from_file(const char *fn) static void load_persistent_properties() { DIR* dir = opendir(PERSISTENT_PROPERTY_DIR); + int dir_fd; struct dirent* entry; - char path[PATH_MAX]; char value[PROP_VALUE_MAX]; int fd, length; + struct stat sb; if (dir) { + dir_fd = dirfd(dir); while ((entry = readdir(dir)) != NULL) { if (strncmp("persist.", entry->d_name, strlen("persist."))) continue; @@ -542,20 +528,39 @@ static void load_persistent_properties() continue; #endif /* open the file and read the property value */ - snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, entry->d_name); - fd = open(path, O_RDONLY); - if (fd >= 0) { - length = read(fd, value, sizeof(value) - 1); - if (length >= 0) { - value[length] = 0; - property_set(entry->d_name, value); - } else { - ERROR("Unable to read persistent property file %s errno: %d\n", path, errno); - } + fd = openat(dir_fd, entry->d_name, O_RDONLY | O_NOFOLLOW); + if (fd < 0) { + ERROR("Unable to open persistent property file \"%s\" errno: %d\n", + entry->d_name, errno); + continue; + } + if (fstat(fd, &sb) < 0) { + ERROR("fstat on property file \"%s\" failed errno: %d\n", entry->d_name, errno); close(fd); + continue; + } + + // File must not be accessible to others, be owned by root/root, and + // not be a hard link to any other file. + if (((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0) + || (sb.st_uid != 0) + || (sb.st_gid != 0) + || (sb.st_nlink != 1)) { + ERROR("skipping insecure property file %s (uid=%lu gid=%lu nlink=%d mode=%o)\n", + entry->d_name, sb.st_uid, sb.st_gid, sb.st_nlink, sb.st_mode); + close(fd); + continue; + } + + length = read(fd, value, sizeof(value) - 1); + if (length >= 0) { + value[length] = 0; + property_set(entry->d_name, value); } else { - ERROR("Unable to open persistent property file %s errno: %d\n", path, errno); + ERROR("Unable to read persistent property file %s errno: %d\n", + entry->d_name, errno); } + close(fd); } closedir(dir); } else { @@ -580,6 +585,16 @@ int properties_inited(void) return property_area_inited; } +static void load_override_properties() { +#ifdef ALLOW_LOCAL_PROP_OVERRIDE + const char *debuggable = property_get("ro.debuggable"); + if (debuggable && (strcmp(debuggable, "1") == 0)) { + load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE); + } +#endif /* ALLOW_LOCAL_PROP_OVERRIDE */ +} + + /* When booting an encrypted system, /data is not mounted when the * property service is started, so any properties stored there are * not loaded. Vold triggers init to load these properties once it @@ -587,9 +602,7 @@ int properties_inited(void) */ void load_persist_props(void) { -#ifdef ALLOW_LOCAL_PROP_OVERRIDE - load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE); -#endif /* ALLOW_LOCAL_PROP_OVERRIDE */ + load_override_properties(); /* Read persistent properties after all default values have been loaded. */ load_persistent_properties(); } @@ -600,9 +613,7 @@ void start_property_service(void) load_properties_from_file(PROP_PATH_SYSTEM_BUILD); load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT); -#ifdef ALLOW_LOCAL_PROP_OVERRIDE - load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE); -#endif /* ALLOW_LOCAL_PROP_OVERRIDE */ + load_override_properties(); /* Read persistent properties after all default values have been loaded. */ load_persistent_properties(); diff --git a/init/readme.txt b/init/readme.txt index fe0d15d..7a5997d 100644 --- a/init/readme.txt +++ b/init/readme.txt @@ -88,6 +88,13 @@ group <groupname> [ <groupname> ]* supplemental groups of the process (via setgroups()). Currently defaults to root. (??? probably should default to nobody) +seclabel <securitycontext> + Change to securitycontext before exec'ing this service. + Primarily for use by services run from the rootfs, e.g. ueventd, adbd. + Services on the system partition can instead use policy-defined transitions + based on their file security context. + If not specified and no transition is defined in policy, defaults to the init context. + oneshot Do not restart the service when it exits. @@ -182,6 +189,21 @@ mount <type> <device> <dir> [ <mountoption> ]* device by name. <mountoption>s include "ro", "rw", "remount", "noatime", ... +restorecon <path> + Restore the file named by <path> to the security context specified + in the file_contexts configuration. + Not required for directories created by the init.rc as these are + automatically labeled correctly by init. + +setcon <securitycontext> + Set the current process security context to the specified string. + This is typically only used from early-init to set the init context + before any other process is started. + +setenforce 0|1 + Set the SELinux system-wide enforcing status. + 0 is permissive (i.e. log but do not deny), 1 is enforcing. + setkey TBD @@ -191,6 +213,10 @@ setprop <name> <value> setrlimit <resource> <cur> <max> Set the rlimit for a resource. +setsebool <name> <value> + Set SELinux boolean <name> to <value>. + <value> may be 1|true|on or 0|false|off + start <service> Start a service running if it is not already running. diff --git a/init/ueventd.c b/init/ueventd.c index a89e067..a41c31e 100644 --- a/init/ueventd.c +++ b/init/ueventd.c @@ -105,7 +105,7 @@ static int get_android_id(const char *id) for (i = 0; i < ARRAY_SIZE(android_ids); i++) if (!strcmp(id, android_ids[i].name)) return android_ids[i].aid; - return 0; + return -1; } void set_device_permission(int nargs, char **args) diff --git a/init/util.c b/init/util.c index 743748b..918bc05 100755 --- a/init/util.c +++ b/init/util.c @@ -23,9 +23,7 @@ #include <errno.h> #include <time.h> -#ifdef HAVE_SELINUX #include <selinux/label.h> -#endif #include <sys/stat.h> #include <sys/types.h> @@ -89,9 +87,7 @@ int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid) { struct sockaddr_un addr; int fd, ret; -#ifdef HAVE_SELINUX char *secon; -#endif fd = socket(PF_UNIX, type, 0); if (fd < 0) { @@ -110,14 +106,12 @@ int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid) goto out_close; } -#ifdef HAVE_SELINUX secon = NULL; if (sehandle) { ret = selabel_lookup(sehandle, &secon, addr.sun_path, S_IFSOCK); if (ret == 0) setfscreatecon(secon); } -#endif ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr)); if (ret) { @@ -125,10 +119,8 @@ int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid) goto out_unlink; } -#ifdef HAVE_SELINUX setfscreatecon(NULL); freecon(secon); -#endif chown(addr.sun_path, uid, gid); chmod(addr.sun_path, perm); @@ -468,31 +460,27 @@ int make_dir(const char *path, mode_t mode) { int rc; -#ifdef HAVE_SELINUX char *secontext = NULL; if (sehandle) { selabel_lookup(sehandle, &secontext, path, mode); setfscreatecon(secontext); } -#endif rc = mkdir(path, mode); -#ifdef HAVE_SELINUX if (secontext) { int save_errno = errno; freecon(secontext); setfscreatecon(NULL); errno = save_errno; } -#endif + return rc; } int restorecon(const char *pathname) { -#ifdef HAVE_SELINUX char *secontext = NULL; struct stat sb; int i; @@ -509,6 +497,5 @@ int restorecon(const char *pathname) return -errno; } freecon(secontext); -#endif return 0; } diff --git a/init/watchdogd.c b/init/watchdogd.c new file mode 100644 index 0000000..fb53836 --- /dev/null +++ b/init/watchdogd.c @@ -0,0 +1,77 @@ +/* + * 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 <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> + +#include <linux/watchdog.h> + +#include "log.h" +#include "util.h" + +#define DEV_NAME "/dev/watchdog" + +int watchdogd_main(int argc, char **argv) +{ + int fd; + int ret; + int interval = 10; + int margin = 10; + int timeout; + + open_devnull_stdio(); + klog_init(); + + INFO("Starting watchdogd\n"); + + if (argc >= 2) + interval = atoi(argv[1]); + + if (argc >= 3) + margin = atoi(argv[2]); + + timeout = interval + margin; + + fd = open(DEV_NAME, O_RDWR); + if (fd < 0) { + ERROR("watchdogd: Failed to open %s: %s\n", DEV_NAME, strerror(errno)); + return 1; + } + + ret = ioctl(fd, WDIOC_SETTIMEOUT, &timeout); + if (ret) { + ERROR("watchdogd: Failed to set timeout to %d: %s\n", timeout, strerror(errno)); + ret = ioctl(fd, WDIOC_GETTIMEOUT, &timeout); + if (ret) { + ERROR("watchdogd: Failed to get timeout: %s\n", strerror(errno)); + } else { + if (timeout > margin) + interval = timeout - margin; + else + interval = 1; + ERROR("watchdogd: Adjusted interval to timeout returned by driver: timeout %d, interval %d, margin %d\n", + timeout, interval, margin); + } + } + + while(1) { + write(fd, "", 1); + sleep(interval); + } +} + diff --git a/init/watchdogd.h b/init/watchdogd.h new file mode 100644 index 0000000..8b48ab8 --- /dev/null +++ b/init/watchdogd.h @@ -0,0 +1,22 @@ +/* + * 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. + */ + +#ifndef _INIT_WATCHDOGD_H_ +#define _INIT_WATCHDOGD_H_ + +int watchdogd_main(int argc, char **argv); + +#endif diff --git a/libcorkscrew/Android.mk b/libcorkscrew/Android.mk index aace5f8..2786d8f 100644 --- a/libcorkscrew/Android.mk +++ b/libcorkscrew/Android.mk @@ -14,9 +14,7 @@ LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := \ +generic_src_files := \ backtrace.c \ backtrace-helper.c \ demangle.c \ @@ -24,16 +22,24 @@ LOCAL_SRC_FILES := \ ptrace.c \ symbol_table.c -ifeq ($(TARGET_ARCH),arm) -LOCAL_SRC_FILES += \ +arm_src_files := \ arch-arm/backtrace-arm.c \ arch-arm/ptrace-arm.c + +x86_src_files := \ + arch-x86/backtrace-x86.c \ + arch-x86/ptrace-x86.c + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(generic_src_files) + +ifeq ($(TARGET_ARCH),arm) +LOCAL_SRC_FILES += $(arm_src_files) LOCAL_CFLAGS += -DCORKSCREW_HAVE_ARCH endif ifeq ($(TARGET_ARCH),x86) -LOCAL_SRC_FILES += \ - arch-x86/backtrace-x86.c \ - arch-x86/ptrace-x86.c +LOCAL_SRC_FILES += $(x86_src_files) LOCAL_CFLAGS += -DCORKSCREW_HAVE_ARCH endif ifeq ($(TARGET_ARCH),mips) @@ -50,3 +56,38 @@ LOCAL_MODULE := libcorkscrew LOCAL_MODULE_TAGS := optional include $(BUILD_SHARED_LIBRARY) + +# Build test. +include $(CLEAR_VARS) +LOCAL_SRC_FILES := test.c +LOCAL_CFLAGS += -std=gnu99 -Werror -fno-inline-small-functions +LOCAL_SHARED_LIBRARIES := libcorkscrew +LOCAL_MODULE := libcorkscrew_test +LOCAL_MODULE_TAGS := optional +include $(BUILD_EXECUTABLE) + + +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_CFLAGS += -std=gnu99 -Werror +LOCAL_MODULE := libcorkscrew +LOCAL_MODULE_TAGS := optional +include $(BUILD_HOST_SHARED_LIBRARY) + +# Build test. +include $(CLEAR_VARS) +LOCAL_SRC_FILES := test.c +LOCAL_CFLAGS += -std=gnu99 -Werror -fno-inline-small-functions +LOCAL_SHARED_LIBRARIES := libcorkscrew +LOCAL_MODULE := libcorkscrew_test +LOCAL_MODULE_TAGS := optional +include $(BUILD_HOST_EXECUTABLE) + +endif # linux-x86 diff --git a/libcorkscrew/arch-arm/ptrace-arm.c b/libcorkscrew/arch-arm/ptrace-arm.c index 868230c..78a9ea9 100644 --- a/libcorkscrew/arch-arm/ptrace-arm.c +++ b/libcorkscrew/arch-arm/ptrace-arm.c @@ -29,12 +29,15 @@ static void load_exidx_header(pid_t pid, map_info_t* mi, uintptr_t* out_exidx_start, size_t* out_exidx_size) { uint32_t elf_phoff; - uint32_t elf_phentsize_phnum; + 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_phentsize_phnum)) { - uint32_t elf_phentsize = elf_phentsize_phnum >> 16; - uint32_t elf_phnum = elf_phentsize_phnum & 0xffff; + &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; 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 @@ static ssize_t unwind_backtrace_common(const memory_t* memory, 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 @@ static ssize_t unwind_backtrace_common(const memory_t* memory, 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 @@ ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo, void* sigcontext, 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 @@ ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context, 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 index 082f635..29159ed 100644..100755 --- a/libcorkscrew/arch-x86/backtrace-x86.c +++ b/libcorkscrew/arch-x86/backtrace-x86.c @@ -23,18 +23,30 @@ #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 <sys/exec_elf.h> #include <cutils/log.h> -#if !defined(__BIONIC_HAVE_UCONTEXT_T) +#if defined(__BIONIC__) + +#if defined(__BIONIC_HAVE_UCONTEXT_T) + +// Bionic offers the Linux kernel headers. +#include <asm/sigcontext.h> +#include <asm/ucontext.h> +typedef struct ucontext ucontext_t; + +#else /* __BIONIC_HAVE_UCONTEXT_T */ + /* Old versions of the Android <signal.h> didn't define ucontext_t. */ typedef struct { @@ -60,47 +72,744 @@ typedef struct ucontext { mcontext_t uc_mcontext; uint32_t uc_sigmask; } ucontext_t; -#endif /* !__BIONIC_HAVE_UCONTEXT_T */ + +#endif /* __BIONIC_HAVE_UCONTEXT_T */ + +#else /* __BIONIC__ */ + +// 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__ */ /* 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. */ +} + +/* 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; + + ptr += *cursor; + + if (ptr < lastptr || lastptr + 3 < ptr) { + lastptr = (ptr >> 2) << 2; + if (!try_get_word(memory, lastptr, &buf)) { + return false; + } + } + *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 __attribute__((unused)), + 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; - for (size_t index = 0; state->ebp && returned_frames < max_depth; index++) { + 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->eip) : state->eip, + index ? rewind_pc_arch(memory, state->reg[DWARF_EIP]) : state->reg[DWARF_EIP], backtrace, ignore_depth, max_depth, &ignored_frames, &returned_frames); - uint32_t next_esp = state->ebp + 8; + + uint32_t stack_top = state->reg[DWARF_ESP]; + + if (!execute_fde(memory, map_info_list, fde, state)) break; + if (frame) { - frame->stack_top = state->esp; - if (state->esp < next_esp) { - frame->stack_size = next_esp - state->esp; + frame->stack_top = stack_top; + if (stack_top < state->reg[DWARF_ESP]) { + frame->stack_size = state->reg[DWARF_ESP] - stack_top; } } - state->esp = next_esp; - if (!try_get_word(memory, state->ebp + 4, &state->eip) - || !try_get_word(memory, state->ebp, &state->ebp) - || !state->eip) { - break; - } + ALOGV("Stack: 0x%x ... 0x%x - %d bytes", frame->stack_top, state->reg[DWARF_ESP], frame->stack_size); } - return returned_frames; } @@ -110,9 +819,9 @@ ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo __attribute__((unused)), const ucontext_t* uc = (const ucontext_t*)sigcontext; unwind_state_t state; - state.ebp = uc->uc_mcontext.gregs[REG_EBP]; - state.eip = uc->uc_mcontext.gregs[REG_EIP]; - state.esp = uc->uc_mcontext.gregs[REG_ESP]; + 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]; memory_t memory; init_memory(&memory, map_info_list); @@ -128,9 +837,9 @@ ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context, } 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); 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 index 07cfd3a..9c49b93 100644..100755 --- 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 e57f428..03dbd53 100644 --- a/libcorkscrew/backtrace.c +++ b/libcorkscrew/backtrace.c @@ -27,14 +27,37 @@ #include <unistd.h> #include <signal.h> +#include <stdlib.h> +#include <string.h> #include <pthread.h> #include <unwind.h> -#include <sys/exec_elf.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> + +#if defined(__BIONIC__) + +// Bionic implements and exports gettid but only implements tgkill. +extern int tgkill(int tgid, int tid, int sig); + +#else + +// glibc doesn't implement or export either gettid or tgkill. + +#include <unistd.h> +#include <sys/syscall.h> + +static pid_t gettid() { + return syscall(__NR_gettid); +} + +static int tgkill(int tgid, int tid, int sig) { + return syscall(__NR_tgkill, tgid, tid, sig); +} + #endif typedef struct { @@ -115,8 +138,6 @@ static void unwind_backtrace_thread_signal_handler(int n __attribute__((unused)) } #endif -extern int tgkill(int tgid, int tid, int sig); - ssize_t unwind_backtrace_thread(pid_t tid, backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) { if (tid == gettid()) { @@ -233,7 +254,6 @@ void get_backtrace_symbols(const backtrace_frame_t* backtrace, size_t frames, 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 @@ -241,7 +261,6 @@ void get_backtrace_symbols(const backtrace_frame_t* backtrace, size_t frames, symbol->symbol_name = strdup(info.dli_sname); symbol->demangled_name = demangle_symbol_name(symbol->symbol_name); } -#endif } } release_my_map_info_list(milist); diff --git a/libcorkscrew/map_info.c b/libcorkscrew/map_info.c index f33378f..6a27664 100644 --- a/libcorkscrew/map_info.c +++ b/libcorkscrew/map_info.c @@ -21,6 +21,7 @@ #include <ctype.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> #include <limits.h> #include <pthread.h> @@ -56,13 +57,15 @@ static map_info_t* parse_maps_line(const char* line) mi->start = start; mi->end = end; mi->is_readable = strlen(permissions) == 4 && permissions[0] == 'r'; + mi->is_writable = strlen(permissions) == 4 && permissions[1] == 'w'; mi->is_executable = strlen(permissions) == 4 && permissions[2] == 'x'; mi->data = NULL; memcpy(mi->name, name, name_len); mi->name[name_len] = '\0'; ALOGV("Parsed map: start=0x%08x, end=0x%08x, " - "is_readable=%d, is_executable=%d, name=%s", - mi->start, mi->end, mi->is_readable, 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; } @@ -109,6 +112,11 @@ bool is_readable_map(const map_info_t* milist, uintptr_t addr) { return mi && mi->is_readable; } +bool is_writable_map(const map_info_t* milist, uintptr_t addr) { + const map_info_t* mi = find_map_info(milist, addr); + return mi && mi->is_writable; +} + bool is_executable_map(const map_info_t* milist, uintptr_t addr) { const map_info_t* mi = find_map_info(milist, addr); return mi && mi->is_executable; diff --git a/libcorkscrew/ptrace-arch.h b/libcorkscrew/ptrace-arch.h index c02df52..4451c29 100644..100755 --- a/libcorkscrew/ptrace-arch.h +++ b/libcorkscrew/ptrace-arch.h @@ -33,6 +33,8 @@ typedef struct { #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 cbea8ca..776ef69 100644 --- a/libcorkscrew/ptrace.c +++ b/libcorkscrew/ptrace.c @@ -21,6 +21,7 @@ #include <corkscrew/ptrace.h> #include <errno.h> +#include <stdlib.h> #include <sys/ptrace.h> #include <cutils/log.h> @@ -128,6 +129,7 @@ void free_ptrace_context(ptrace_context_t* context) { 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 1b97180..29e4a79 100644 --- a/libcorkscrew/symbol_table.c +++ b/libcorkscrew/symbol_table.c @@ -19,14 +19,22 @@ #include <corkscrew/symbol_table.h> +#include <stdbool.h> #include <stdlib.h> +#include <elf.h> #include <fcntl.h> #include <string.h> #include <sys/stat.h> #include <sys/mman.h> -#include <sys/exec_elf.h> #include <cutils/log.h> +static bool is_elf(Elf32_Ehdr* e) { + return (e->e_ident[EI_MAG0] == ELFMAG0 && + e->e_ident[EI_MAG1] == ELFMAG1 && + e->e_ident[EI_MAG2] == ELFMAG2 && + e->e_ident[EI_MAG3] == ELFMAG3); +} + // Compare function for qsort static int qcompar(const void *a, const void *b) { const symbol_t* asym = (const symbol_t*)a; @@ -67,7 +75,7 @@ symbol_table_t* load_symbol_table(const char *filename) { // Parse the file header Elf32_Ehdr *hdr = (Elf32_Ehdr*)base; - if (!IS_ELF(*hdr)) { + if (!is_elf(hdr)) { goto out_close; } Elf32_Shdr *shdr = (Elf32_Shdr*)(base + hdr->e_shoff); diff --git a/libcorkscrew/test.c b/libcorkscrew/test.c new file mode 100644 index 0000000..af34c03 --- /dev/null +++ b/libcorkscrew/test.c @@ -0,0 +1,64 @@ +#include <corkscrew/backtrace.h> +#include <corkscrew/symbol_table.h> +#include <stdio.h> +#include <stdlib.h> + +void do_backtrace() { + 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); + + backtrace_symbol_t* backtrace_symbols = (backtrace_symbol_t*) malloc(sizeof(backtrace_symbol_t) * frame_count); + get_backtrace_symbols(frames, frame_count, backtrace_symbols); + + for (size_t i = 0; i < (size_t) frame_count; ++i) { + char line[MAX_BACKTRACE_LINE_LENGTH]; + format_backtrace_line(i, &frames[i], &backtrace_symbols[i], + line, MAX_BACKTRACE_LINE_LENGTH); + if (backtrace_symbols[i].symbol_name != NULL) { + // get_backtrace_symbols found the symbol's name with dladdr(3). + fprintf(stderr, " %s\n", line); + } else { + // We don't have a symbol. Maybe this is a static symbol, and + // we can look it up? + symbol_table_t* symbols = NULL; + if (backtrace_symbols[i].map_name != NULL) { + symbols = load_symbol_table(backtrace_symbols[i].map_name); + } + const symbol_t* symbol = NULL; + if (symbols != NULL) { + symbol = find_symbol(symbols, frames[i].absolute_pc); + } + if (symbol != NULL) { + uintptr_t offset = frames[i].absolute_pc - symbol->start; + fprintf(stderr, " %s (%s%+d)\n", line, symbol->name, offset); + } else { + fprintf(stderr, " %s (\?\?\?)\n", line); + } + free_symbol_table(symbols); + } + } + + free_backtrace_symbols(backtrace_symbols, frame_count); + free(backtrace_symbols); + free(frames); +} + +__attribute__ ((noinline)) void g() { + fprintf(stderr, "g()\n"); + do_backtrace(); +} + +__attribute__ ((noinline)) int f(int i) { + fprintf(stderr, "f(%i)\n", i); + if (i == 0) { + g(); + return 0; + } + return f(i - 1); +} + +int main() { + return f(5); +} diff --git a/libcutils/Android.mk b/libcutils/Android.mk index ac07d9b..17b320f 100644 --- a/libcutils/Android.mk +++ b/libcutils/Android.mk @@ -50,7 +50,7 @@ commonSources := \ threads.c \ sched_policy.c \ iosched_policy.c \ - str_parms.c + str_parms.c \ commonHostSources := \ ashmem-host.c @@ -75,12 +75,10 @@ ifeq ($(WINDOWS_HOST_ONLY),1) else commonSources += \ abort_socket.c \ + fs.c \ selector.c \ - tztime.c \ + multiuser.c \ zygote.c - - commonHostSources += \ - tzstrftime.c endif @@ -160,3 +158,5 @@ LOCAL_SRC_FILES := str_parms.c hashmap.c memory.c LOCAL_SHARED_LIBRARIES := liblog LOCAL_MODULE_TAGS := optional include $(BUILD_EXECUTABLE) + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/libcutils/atomic.c b/libcutils/atomic.c index f6cd8b0..1484ef8 100644 --- a/libcutils/atomic.c +++ b/libcutils/atomic.c @@ -14,6 +14,6 @@ * limitations under the License. */ -#define inline +#define ANDROID_ATOMIC_INLINE #include <cutils/atomic-inline.h> diff --git a/libcutils/fs.c b/libcutils/fs.c new file mode 100644 index 0000000..1226d44 --- /dev/null +++ b/libcutils/fs.c @@ -0,0 +1,142 @@ +/* + * 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. + */ + +#define LOG_TAG "cutils" + +#include <cutils/fs.h> +#include <cutils/log.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <limits.h> + +#define ALL_PERMS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO) +#define BUF_SIZE 64 + +int fs_prepare_dir(const char* path, mode_t mode, uid_t uid, gid_t gid) { + // Check if path needs to be created + struct stat sb; + if (TEMP_FAILURE_RETRY(lstat(path, &sb)) == -1) { + if (errno == ENOENT) { + goto create; + } else { + ALOGE("Failed to lstat(%s): %s", path, strerror(errno)); + return -1; + } + } + + // Exists, verify status + if (!S_ISDIR(sb.st_mode)) { + ALOGE("Not a directory: %s", path); + return -1; + } + if (((sb.st_mode & ALL_PERMS) == mode) && (sb.st_uid == uid) && (sb.st_gid == gid)) { + return 0; + } else { + goto fixup; + } + +create: + if (TEMP_FAILURE_RETRY(mkdir(path, mode)) == -1) { + if (errno != EEXIST) { + ALOGE("Failed to mkdir(%s): %s", path, strerror(errno)); + return -1; + } + } + +fixup: + if (TEMP_FAILURE_RETRY(chmod(path, mode)) == -1) { + ALOGE("Failed to chmod(%s, %d): %s", path, mode, strerror(errno)); + return -1; + } + if (TEMP_FAILURE_RETRY(chown(path, uid, gid)) == -1) { + ALOGE("Failed to chown(%s, %d, %d): %s", path, uid, gid, strerror(errno)); + return -1; + } + + return 0; +} + +int fs_read_atomic_int(const char* path, int* out_value) { + int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY)); + if (fd == -1) { + ALOGE("Failed to read %s: %s", path, strerror(errno)); + return -1; + } + + char buf[BUF_SIZE]; + if (TEMP_FAILURE_RETRY(read(fd, buf, BUF_SIZE)) == -1) { + ALOGE("Failed to read %s: %s", path, strerror(errno)); + goto fail; + } + if (sscanf(buf, "%d", out_value) != 1) { + ALOGE("Failed to parse %s: %s", path, strerror(errno)); + goto fail; + } + close(fd); + return 0; + +fail: + close(fd); + *out_value = -1; + return -1; +} + +int fs_write_atomic_int(const char* path, int value) { + char temp[PATH_MAX]; + if (snprintf(temp, PATH_MAX, "%s.XXXXXX", path) >= PATH_MAX) { + ALOGE("Path too long"); + return -1; + } + + int fd = TEMP_FAILURE_RETRY(mkstemp(temp)); + if (fd == -1) { + ALOGE("Failed to open %s: %s", temp, strerror(errno)); + return -1; + } + + char buf[BUF_SIZE]; + int len = snprintf(buf, BUF_SIZE, "%d", value) + 1; + if (len > BUF_SIZE) { + ALOGE("Value %d too large: %s", value, strerror(errno)); + goto fail; + } + if (TEMP_FAILURE_RETRY(write(fd, buf, len)) < len) { + ALOGE("Failed to write %s: %s", temp, strerror(errno)); + goto fail; + } + if (close(fd) == -1) { + ALOGE("Failed to close %s: %s", temp, strerror(errno)); + goto fail_closed; + } + + if (rename(temp, path) == -1) { + ALOGE("Failed to rename %s to %s: %s", temp, path, strerror(errno)); + goto fail_closed; + } + + return 0; + +fail: + close(fd); +fail_closed: + unlink(temp); + return -1; +} diff --git a/libcutils/multiuser.c b/libcutils/multiuser.c new file mode 100644 index 0000000..7c74bb8 --- /dev/null +++ b/libcutils/multiuser.c @@ -0,0 +1,29 @@ +/* + * 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/multiuser.h> + +userid_t multiuser_get_user_id(uid_t uid) { + return uid / MULTIUSER_APP_PER_USER_RANGE; +} + +appid_t multiuser_get_app_id(uid_t uid) { + return uid % MULTIUSER_APP_PER_USER_RANGE; +} + +uid_t multiuser_get_uid(userid_t userId, appid_t appId) { + return userId * MULTIUSER_APP_PER_USER_RANGE + (appId % MULTIUSER_APP_PER_USER_RANGE); +} 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/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/libion/ion.c b/libion/ion.c index dbeac23..020c35b 100644 --- a/libion/ion.c +++ b/libion/ion.c @@ -54,13 +54,14 @@ static int ion_ioctl(int fd, int req, void *arg) return ret; } -int ion_alloc(int fd, size_t len, size_t align, unsigned int flags, - struct ion_handle **handle) +int ion_alloc(int fd, size_t len, size_t align, unsigned int heap_mask, + unsigned int flags, struct ion_handle **handle) { int ret; struct ion_allocation_data data = { .len = len, .align = align, + .heap_mask = heap_mask, .flags = flags, }; @@ -120,6 +121,19 @@ int ion_share(int fd, struct ion_handle *handle, int *share_fd) return ret; } +int ion_alloc_fd(int fd, size_t len, size_t align, unsigned int heap_mask, + unsigned int flags, int *handle_fd) { + struct ion_handle *handle; + int ret; + + ret = ion_alloc(fd, len, align, heap_mask, flags, &handle); + if (ret < 0) + return ret; + ret = ion_share(fd, handle, handle_fd); + ion_free(fd, handle); + return ret; +} + int ion_import(int fd, int share_fd, struct ion_handle **handle) { struct ion_fd_data data = { @@ -132,3 +146,11 @@ int ion_import(int fd, int share_fd, struct ion_handle **handle) *handle = data.handle; return ret; } + +int ion_sync_fd(int fd, int handle_fd) +{ + struct ion_fd_data data = { + .fd = handle_fd, + }; + return ion_ioctl(fd, ION_IOC_SYNC, &data); +} diff --git a/libion/ion_test.c b/libion/ion_test.c index 3f2d7cc..0caaa2a 100644 --- a/libion/ion_test.c +++ b/libion/ion_test.c @@ -19,8 +19,8 @@ size_t len = 1024*1024, align = 0; int prot = PROT_READ | PROT_WRITE; int map_flags = MAP_SHARED; int alloc_flags = 0; +int heap_mask = 1; int test = -1; -size_t width = 1024*1024, height = 1024*1024; size_t stride; int _ion_alloc_test(int *fd, struct ion_handle **handle) @@ -31,7 +31,7 @@ int _ion_alloc_test(int *fd, struct ion_handle **handle) if (*fd < 0) return *fd; - ret = ion_alloc(*fd, len, align, alloc_flags, handle); + ret = ion_alloc(*fd, len, align, heap_mask, alloc_flags, handle); if (ret) printf("%s failed: %s\n", __func__, strerror(ret)); @@ -203,17 +203,16 @@ int main(int argc, char* argv[]) { static struct option opts[] = { {"alloc", no_argument, 0, 'a'}, {"alloc_flags", required_argument, 0, 'f'}, + {"heap_mask", required_argument, 0, 'h'}, {"map", no_argument, 0, 'm'}, {"share", no_argument, 0, 's'}, {"len", required_argument, 0, 'l'}, {"align", required_argument, 0, 'g'}, {"map_flags", required_argument, 0, 'z'}, {"prot", required_argument, 0, 'p'}, - {"width", required_argument, 0, 'w'}, - {"height", required_argument, 0, 'h'}, }; int i = 0; - c = getopt_long(argc, argv, "af:h:l:mr:stw:", opts, &i); + c = getopt_long(argc, argv, "af:h:l:mr:st", opts, &i); if (c == -1) break; @@ -245,6 +244,9 @@ int main(int argc, char* argv[]) { case 'f': alloc_flags = atol(optarg); break; + case 'h': + heap_mask = atol(optarg); + break; case 'a': test = ALLOC_TEST; break; @@ -254,17 +256,11 @@ int main(int argc, char* argv[]) { case 's': test = SHARE_TEST; break; - case 'w': - width = atol(optarg); - break; - case 'h': - height = atol(optarg); - break; } } - printf("test %d, len %u, width %u, height %u align %u, " - "map_flags %d, prot %d, alloc_flags %d\n", test, len, width, - height, align, map_flags, prot, alloc_flags); + printf("test %d, len %u, align %u, map_flags %d, prot %d, heap_mask %d," + " alloc_flags %d\n", test, len, align, map_flags, prot, + heap_mask, alloc_flags); switch (test) { case ALLOC_TEST: ion_alloc_test(); diff --git a/liblog/logd_write.c b/liblog/logd_write.c index a0a753b..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 @@ -134,6 +136,7 @@ int __android_log_write(int prio, const char *tag, const char *msg) { struct iovec vec[3]; log_id_t log_id = LOG_ID_MAIN; + char tmp_tag[32]; if (!tag) tag = ""; @@ -141,13 +144,18 @@ int __android_log_write(int prio, const char *tag, const char *msg) /* XXX: This needs to go! */ if (!strcmp(tag, "HTC_RIL") || !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */ + !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */ !strcmp(tag, "AT") || !strcmp(tag, "GSM") || !strcmp(tag, "STK") || !strcmp(tag, "CDMA") || !strcmp(tag, "PHONE") || - !strcmp(tag, "SMS")) + !strcmp(tag, "SMS")) { log_id = LOG_ID_RADIO; + // Inform third party apps/ril/radio.. to use Rlog or RLOG + snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag); + tag = tmp_tag; + } vec[0].iov_base = (unsigned char *) &prio; vec[0].iov_len = 1; @@ -162,20 +170,27 @@ int __android_log_write(int prio, const char *tag, const char *msg) int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg) { struct iovec vec[3]; + char tmp_tag[32]; if (!tag) tag = ""; /* XXX: This needs to go! */ - if (!strcmp(tag, "HTC_RIL") || + if ((bufID != LOG_ID_RADIO) && + (!strcmp(tag, "HTC_RIL") || !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */ + !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */ !strcmp(tag, "AT") || !strcmp(tag, "GSM") || !strcmp(tag, "STK") || !strcmp(tag, "CDMA") || !strcmp(tag, "PHONE") || - !strcmp(tag, "SMS")) + !strcmp(tag, "SMS"))) { bufID = LOG_ID_RADIO; + // Inform third party apps/ril/radio.. to use Rlog or RLOG + snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag); + tag = tmp_tag; + } vec[0].iov_base = (unsigned char *) &prio; vec[0].iov_len = 1; diff --git a/libmincrypt/Android.mk b/libmincrypt/Android.mk index b09a941..f58eab9 100644 --- a/libmincrypt/Android.mk +++ b/libmincrypt/Android.mk @@ -4,13 +4,13 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := libmincrypt -LOCAL_SRC_FILES := rsa.c sha.c +LOCAL_SRC_FILES := rsa.c rsa_e_3.c rsa_e_f4.c sha.c include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := libmincrypt -LOCAL_SRC_FILES := rsa.c sha.c +LOCAL_SRC_FILES := rsa.c rsa_e_3.c rsa_e_f4.c sha.c include $(BUILD_HOST_STATIC_LIBRARY) diff --git a/libmincrypt/rsa.c b/libmincrypt/rsa.c index d7124fb..b4ee6af 100644 --- a/libmincrypt/rsa.c +++ b/libmincrypt/rsa.c @@ -1,6 +1,6 @@ /* rsa.c ** -** Copyright 2008, The Android Open Source Project +** Copyright 2012, The Android Open Source Project ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are met: @@ -13,186 +13,42 @@ ** be used to endorse or promote products derived from this software ** without specific prior written permission. ** -** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR +** THIS SOFTWARE IS PROVIDED BY Google 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 +** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO ** EVENT SHALL Google 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 +** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR ** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "mincrypt/rsa.h" -#include "mincrypt/sha.h" -/* a[] -= mod */ -static void subM(const RSAPublicKey *key, uint32_t *a) { - int64_t A = 0; - int i; - for (i = 0; i < key->len; ++i) { - A += (uint64_t)a[i] - key->n[i]; - a[i] = (uint32_t)A; - A >>= 32; - } -} - -/* return a[] >= mod */ -static int geM(const RSAPublicKey *key, const uint32_t *a) { - int i; - for (i = key->len; i;) { - --i; - if (a[i] < key->n[i]) return 0; - if (a[i] > key->n[i]) return 1; - } - return 1; /* equal */ -} - -/* montgomery c[] += a * b[] / R % mod */ -static void montMulAdd(const RSAPublicKey *key, - uint32_t* c, - const uint32_t a, - const uint32_t* b) { - uint64_t A = (uint64_t)a * b[0] + c[0]; - uint32_t d0 = (uint32_t)A * key->n0inv; - uint64_t B = (uint64_t)d0 * key->n[0] + (uint32_t)A; - int i; - - for (i = 1; i < key->len; ++i) { - A = (A >> 32) + (uint64_t)a * b[i] + c[i]; - B = (B >> 32) + (uint64_t)d0 * key->n[i] + (uint32_t)A; - c[i - 1] = (uint32_t)B; - } - - A = (A >> 32) + (B >> 32); - - c[i - 1] = (uint32_t)A; - - if (A >> 32) { - subM(key, c); - } -} - -/* montgomery c[] = a[] * b[] / R % mod */ -static void montMul(const RSAPublicKey *key, - uint32_t* c, - const uint32_t* a, - const uint32_t* b) { - int i; - for (i = 0; i < key->len; ++i) { - c[i] = 0; - } - for (i = 0; i < key->len; ++i) { - montMulAdd(key, c, a[i], b); - } -} - -/* In-place public exponentiation. -** Input and output big-endian byte array in inout. -*/ -static void modpow3(const RSAPublicKey *key, - uint8_t* inout) { - uint32_t a[RSANUMWORDS]; - uint32_t aR[RSANUMWORDS]; - uint32_t aaR[RSANUMWORDS]; - uint32_t *aaa = aR; /* Re-use location. */ - int i; - - /* Convert from big endian byte array to little endian word array. */ - for (i = 0; i < key->len; ++i) { - uint32_t tmp = - (inout[((key->len - 1 - i) * 4) + 0] << 24) | - (inout[((key->len - 1 - i) * 4) + 1] << 16) | - (inout[((key->len - 1 - i) * 4) + 2] << 8) | - (inout[((key->len - 1 - i) * 4) + 3] << 0); - a[i] = tmp; - } +int RSA_e_f4_verify(const RSAPublicKey* key, + const uint8_t* signature, + const int len, + const uint8_t* sha); - montMul(key, aR, a, key->rr); /* aR = a * RR / R mod M */ - montMul(key, aaR, aR, aR); /* aaR = aR * aR / R mod M */ - montMul(key, aaa, aaR, a); /* aaa = aaR * a / R mod M */ +int RSA_e_3_verify(const RSAPublicKey *key, + const uint8_t *signature, + const int len, + const uint8_t *sha); - /* Make sure aaa < mod; aaa is at most 1x mod too large. */ - if (geM(key, aaa)) { - subM(key, aaa); - } - - /* Convert to bigendian byte array */ - for (i = key->len - 1; i >= 0; --i) { - uint32_t tmp = aaa[i]; - *inout++ = tmp >> 24; - *inout++ = tmp >> 16; - *inout++ = tmp >> 8; - *inout++ = tmp >> 0; - } -} - -/* Expected PKCS1.5 signature padding bytes, for a keytool RSA signature. -** Has the 0-length optional parameter encoded in the ASN1 (as opposed to the -** other flavor which omits the optional parameter entirely). This code does not -** accept signatures without the optional parameter. -*/ -static const uint8_t padding[RSANUMBYTES - SHA_DIGEST_SIZE] = { - 0x00,0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00, - 0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0e,0x03,0x02,0x1a,0x05,0x00, - 0x04,0x14 -}; - -/* Verify a 2048 bit RSA PKCS1.5 signature against an expected SHA-1 hash. -** Returns 0 on failure, 1 on success. -*/ int RSA_verify(const RSAPublicKey *key, const uint8_t *signature, const int len, const uint8_t *sha) { - uint8_t buf[RSANUMBYTES]; - int i; - - if (key->len != RSANUMWORDS) { - return 0; /* Wrong key passed in. */ - } - - if (len != sizeof(buf)) { - return 0; /* Wrong input length. */ - } - - for (i = 0; i < len; ++i) { - buf[i] = signature[i]; - } - - modpow3(key, buf); - - /* Check pkcs1.5 padding bytes. */ - for (i = 0; i < (int) sizeof(padding); ++i) { - if (buf[i] != padding[i]) { + switch (key->exponent) { + case 3: + return RSA_e_3_verify(key, signature, len, sha); + break; + case 65537: + return RSA_e_f4_verify(key, signature, len, sha); + break; + default: return 0; - } } - - /* Check sha digest matches. */ - for (; i < len; ++i) { - if (buf[i] != *sha++) { - return 0; - } - } - - return 1; } diff --git a/libmincrypt/rsa_e_3.c b/libmincrypt/rsa_e_3.c new file mode 100644 index 0000000..c8c02c4 --- /dev/null +++ b/libmincrypt/rsa_e_3.c @@ -0,0 +1,202 @@ +/* rsa_e_3.c +** +** Copyright 2008, The Android Open Source Project +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of Google Inc. nor the names of its contributors may +** be used to endorse or promote products derived from this software +** without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY Google 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 Google 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. +*/ + +#include "mincrypt/rsa.h" +#include "mincrypt/sha.h" + +/* a[] -= mod */ +static void subM(const RSAPublicKey *key, uint32_t *a) { + int64_t A = 0; + int i; + for (i = 0; i < key->len; ++i) { + A += (uint64_t)a[i] - key->n[i]; + a[i] = (uint32_t)A; + A >>= 32; + } +} + +/* return a[] >= mod */ +static int geM(const RSAPublicKey *key, const uint32_t *a) { + int i; + for (i = key->len; i;) { + --i; + if (a[i] < key->n[i]) return 0; + if (a[i] > key->n[i]) return 1; + } + return 1; /* equal */ +} + +/* montgomery c[] += a * b[] / R % mod */ +static void montMulAdd(const RSAPublicKey *key, + uint32_t* c, + const uint32_t a, + const uint32_t* b) { + uint64_t A = (uint64_t)a * b[0] + c[0]; + uint32_t d0 = (uint32_t)A * key->n0inv; + uint64_t B = (uint64_t)d0 * key->n[0] + (uint32_t)A; + int i; + + for (i = 1; i < key->len; ++i) { + A = (A >> 32) + (uint64_t)a * b[i] + c[i]; + B = (B >> 32) + (uint64_t)d0 * key->n[i] + (uint32_t)A; + c[i - 1] = (uint32_t)B; + } + + A = (A >> 32) + (B >> 32); + + c[i - 1] = (uint32_t)A; + + if (A >> 32) { + subM(key, c); + } +} + +/* montgomery c[] = a[] * b[] / R % mod */ +static void montMul(const RSAPublicKey *key, + uint32_t* c, + const uint32_t* a, + const uint32_t* b) { + int i; + for (i = 0; i < key->len; ++i) { + c[i] = 0; + } + for (i = 0; i < key->len; ++i) { + montMulAdd(key, c, a[i], b); + } +} + +/* In-place public exponentiation. +** Input and output big-endian byte array in inout. +*/ +static void modpow3(const RSAPublicKey *key, + uint8_t* inout) { + uint32_t a[RSANUMWORDS]; + uint32_t aR[RSANUMWORDS]; + uint32_t aaR[RSANUMWORDS]; + uint32_t *aaa = aR; /* Re-use location. */ + int i; + + /* Convert from big endian byte array to little endian word array. */ + for (i = 0; i < key->len; ++i) { + uint32_t tmp = + (inout[((key->len - 1 - i) * 4) + 0] << 24) | + (inout[((key->len - 1 - i) * 4) + 1] << 16) | + (inout[((key->len - 1 - i) * 4) + 2] << 8) | + (inout[((key->len - 1 - i) * 4) + 3] << 0); + a[i] = tmp; + } + + montMul(key, aR, a, key->rr); /* aR = a * RR / R mod M */ + montMul(key, aaR, aR, aR); /* aaR = aR * aR / R mod M */ + montMul(key, aaa, aaR, a); /* aaa = aaR * a / R mod M */ + + /* Make sure aaa < mod; aaa is at most 1x mod too large. */ + if (geM(key, aaa)) { + subM(key, aaa); + } + + /* Convert to bigendian byte array */ + for (i = key->len - 1; i >= 0; --i) { + uint32_t tmp = aaa[i]; + *inout++ = tmp >> 24; + *inout++ = tmp >> 16; + *inout++ = tmp >> 8; + *inout++ = tmp >> 0; + } +} + +/* Expected PKCS1.5 signature padding bytes, for a keytool RSA signature. +** Has the 0-length optional parameter encoded in the ASN1 (as opposed to the +** other flavor which omits the optional parameter entirely). This code does not +** accept signatures without the optional parameter. +*/ +static const uint8_t padding[RSANUMBYTES - SHA_DIGEST_SIZE] = { + 0x00,0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00, + 0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0e,0x03,0x02,0x1a,0x05,0x00, + 0x04,0x14 +}; + +/* Verify a 2048 bit RSA e=3 PKCS1.5 signature against an expected SHA-1 hash. +** Returns 0 on failure, 1 on success. +*/ +int RSA_e_3_verify(const RSAPublicKey *key, + const uint8_t *signature, + const int len, + const uint8_t *sha) { + uint8_t buf[RSANUMBYTES]; + int i; + + if (key->len != RSANUMWORDS) { + return 0; /* Wrong key passed in. */ + } + + if (len != sizeof(buf)) { + return 0; /* Wrong input length. */ + } + + if (key->exponent != 3) { + return 0; // Wrong exponent. + } + + for (i = 0; i < len; ++i) { + buf[i] = signature[i]; + } + + modpow3(key, buf); + + /* Check pkcs1.5 padding bytes. */ + for (i = 0; i < (int) sizeof(padding); ++i) { + if (buf[i] != padding[i]) { + return 0; + } + } + + /* Check sha digest matches. */ + for (; i < len; ++i) { + if (buf[i] != *sha++) { + return 0; + } + } + + return 1; +} diff --git a/libmincrypt/rsa_e_f4.c b/libmincrypt/rsa_e_f4.c new file mode 100644 index 0000000..6701bcc --- /dev/null +++ b/libmincrypt/rsa_e_f4.c @@ -0,0 +1,196 @@ +/* rsa_e_f4.c +** +** Copyright 2012, The Android Open Source Project +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of Google Inc. nor the names of its contributors may +** be used to endorse or promote products derived from this software +** without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY Google 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 Google 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. +*/ + +#include "mincrypt/rsa.h" +#include "mincrypt/sha.h" + +// a[] -= mod +static void subM(const RSAPublicKey* key, + uint32_t* a) { + int64_t A = 0; + int i; + for (i = 0; i < key->len; ++i) { + A += (uint64_t)a[i] - key->n[i]; + a[i] = (uint32_t)A; + A >>= 32; + } +} + +// return a[] >= mod +static int geM(const RSAPublicKey* key, + const uint32_t* a) { + int i; + for (i = key->len; i;) { + --i; + if (a[i] < key->n[i]) return 0; + if (a[i] > key->n[i]) return 1; + } + return 1; // equal +} + +// montgomery c[] += a * b[] / R % mod +static void montMulAdd(const RSAPublicKey* key, + uint32_t* c, + const uint32_t a, + const uint32_t* b) { + uint64_t A = (uint64_t)a * b[0] + c[0]; + uint32_t d0 = (uint32_t)A * key->n0inv; + uint64_t B = (uint64_t)d0 * key->n[0] + (uint32_t)A; + int i; + + for (i = 1; i < key->len; ++i) { + A = (A >> 32) + (uint64_t)a * b[i] + c[i]; + B = (B >> 32) + (uint64_t)d0 * key->n[i] + (uint32_t)A; + c[i - 1] = (uint32_t)B; + } + + A = (A >> 32) + (B >> 32); + + c[i - 1] = (uint32_t)A; + + if (A >> 32) { + subM(key, c); + } +} + +// montgomery c[] = a[] * b[] / R % mod +static void montMul(const RSAPublicKey* key, + uint32_t* c, + const uint32_t* a, + const uint32_t* b) { + int i; + for (i = 0; i < key->len; ++i) { + c[i] = 0; + } + for (i = 0; i < key->len; ++i) { + montMulAdd(key, c, a[i], b); + } +} + +// In-place public exponentiation. +// Input and output big-endian byte array in inout. +static void modpowF4(const RSAPublicKey* key, + uint8_t* inout) { + uint32_t a[RSANUMWORDS]; + uint32_t aR[RSANUMWORDS]; + uint32_t aaR[RSANUMWORDS]; + uint32_t* aaa = aaR; // Re-use location. + int i; + + // Convert from big endian byte array to little endian word array. + for (i = 0; i < key->len; ++i) { + uint32_t tmp = + (inout[((key->len - 1 - i) * 4) + 0] << 24) | + (inout[((key->len - 1 - i) * 4) + 1] << 16) | + (inout[((key->len - 1 - i) * 4) + 2] << 8) | + (inout[((key->len - 1 - i) * 4) + 3] << 0); + a[i] = tmp; + } + + montMul(key, aR, a, key->rr); // aR = a * RR / R mod M + for (i = 0; i < 16; i += 2) { + montMul(key, aaR, aR, aR); // aaR = aR * aR / R mod M + montMul(key, aR, aaR, aaR); // aR = aaR * aaR / R mod M + } + montMul(key, aaa, aR, a); // aaa = aR * a / R mod M + + // Make sure aaa < mod; aaa is at most 1x mod too large. + if (geM(key, aaa)) { + subM(key, aaa); + } + + // Convert to bigendian byte array + for (i = key->len - 1; i >= 0; --i) { + uint32_t tmp = aaa[i]; + *inout++ = tmp >> 24; + *inout++ = tmp >> 16; + *inout++ = tmp >> 8; + *inout++ = tmp >> 0; + } +} + +// Expected PKCS1.5 signature padding bytes, for a keytool RSA signature. +// Has the 0-length optional parameter encoded in the ASN1 (as opposed to the +// other flavor which omits the optional parameter entirely). This code does not +// accept signatures without the optional parameter. +/* +static const uint8_t padding[RSANUMBYTES] = { +0x00,0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0e,0x03,0x02,0x1a,0x05,0x00,0x04,0x14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; +*/ + +// SHA-1 of PKCS1.5 signature padding for 2048 bit, as above. +// At the location of the bytes of the hash all 00 are hashed. +static const uint8_t kExpectedPadShaRsa2048[SHA_DIGEST_SIZE] = { + 0xdc, 0xbd, 0xbe, 0x42, 0xd5, 0xf5, 0xa7, 0x2e, 0x6e, 0xfc, + 0xf5, 0x5d, 0xaf, 0x9d, 0xea, 0x68, 0x7c, 0xfb, 0xf1, 0x67 +}; + +// Verify a 2048 bit RSA e=65537 PKCS1.5 signature against an expected +// SHA-1 hash. Returns 0 on failure, 1 on success. +int RSA_e_f4_verify(const RSAPublicKey* key, + const uint8_t* signature, + const int len, + const uint8_t* sha) { + uint8_t buf[RSANUMBYTES]; + int i; + + if (key->len != RSANUMWORDS) { + return 0; // Wrong key passed in. + } + + if (len != sizeof(buf)) { + return 0; // Wrong input length. + } + + if (key->exponent != 65537) { + return 0; // Wrong exponent. + } + + for (i = 0; i < len; ++i) { // Copy input to local workspace. + buf[i] = signature[i]; + } + + modpowF4(key, buf); // In-place exponentiation. + + // Xor sha portion, so it all becomes 00 iff equal. + for (i = len - SHA_DIGEST_SIZE; i < len; ++i) { + buf[i] ^= *sha++; + } + + // Hash resulting buf, in-place. + SHA(buf, len, buf); + + // Compare against expected hash value. + for (i = 0; i < SHA_DIGEST_SIZE; ++i) { + if (buf[i] != kExpectedPadShaRsa2048[i]) { + return 0; + } + } + + return 1; // All checked out OK. +} diff --git a/libmincrypt/tools/DumpPublicKey.java b/libmincrypt/tools/DumpPublicKey.java index d2935e0..12b4f56 100644 --- a/libmincrypt/tools/DumpPublicKey.java +++ b/libmincrypt/tools/DumpPublicKey.java @@ -24,7 +24,6 @@ import java.security.KeyStore; import java.security.Key; import java.security.PublicKey; import java.security.interfaces.RSAPublicKey; -import sun.misc.BASE64Encoder; /** * Command line tool to extract RSA public keys from X.509 certificates @@ -34,27 +33,42 @@ import sun.misc.BASE64Encoder; class DumpPublicKey { /** * @param key to perform sanity checks on + * @return version number of key. Supported versions are: + * 1: 2048-bit key with e=3 + * 2: 2048-bit key with e=65537 * @throws Exception if the key has the wrong size or public exponent + */ - static void check(RSAPublicKey key) throws Exception { + static int check(RSAPublicKey key) throws Exception { BigInteger pubexp = key.getPublicExponent(); BigInteger modulus = key.getModulus(); + int version; + + if (pubexp.equals(BigInteger.valueOf(3))) { + version = 1; + } else if (pubexp.equals(BigInteger.valueOf(65537))) { + version = 2; + } else { + throw new Exception("Public exponent should be 3 or 65537 but is " + + pubexp.toString(10) + "."); + } - if (!pubexp.equals(BigInteger.valueOf(3))) - throw new Exception("Public exponent should be 3 but is " + - pubexp.toString(10) + "."); - - if (modulus.bitLength() != 2048) + if (modulus.bitLength() != 2048) { throw new Exception("Modulus should be 2048 bits long but is " + modulus.bitLength() + " bits."); + } + + return version; } /** * @param key to output - * @return a C initializer representing this public key. + * @return a String representing this public key. If the key is a + * version 1 key, the string will be a C initializer; this is + * not true for newer key versions. */ static String print(RSAPublicKey key) throws Exception { - check(key); + int version = check(key); BigInteger N = key.getModulus(); @@ -62,6 +76,12 @@ class DumpPublicKey { int nwords = N.bitLength() / 32; // # of 32 bit integers in modulus + if (version > 1) { + result.append("v"); + result.append(Integer.toString(version)); + result.append(" "); + } + result.append("{"); result.append(nwords); diff --git a/libnetutils/dhcp_utils.c b/libnetutils/dhcp_utils.c index 903a1db..b4caaf9 100644 --- a/libnetutils/dhcp_utils.c +++ b/libnetutils/dhcp_utils.c @@ -29,6 +29,7 @@ static const char DAEMON_NAME[] = "dhcpcd"; static const char DAEMON_PROP_NAME[] = "init.svc.dhcpcd"; static const char HOSTNAME_PROP_NAME[] = "net.hostname"; static const char DHCP_PROP_NAME_PREFIX[] = "dhcp"; +static const char DHCP_CONFIG_PATH[] = "/system/etc/dhcpcd/dhcpcd.conf"; static const int NAP_TIME = 200; /* wait for 200ms at a time */ /* when polling for property values */ static const char DAEMON_NAME_RENEW[] = "iprenew"; @@ -170,7 +171,7 @@ static const char *ipaddr_to_string(in_addr_t addr) * The device init.rc file needs a corresponding entry for this work. * * Example: - * service dhcpcd_<interface> /system/bin/dhcpcd -ABKL + * service dhcpcd_<interface> /system/bin/dhcpcd -ABKL -f dhcpcd.conf */ int dhcp_do_request(const char *interface, char *ipaddr, @@ -185,7 +186,7 @@ int dhcp_do_request(const char *interface, char result_prop_name[PROPERTY_KEY_MAX]; char daemon_prop_name[PROPERTY_KEY_MAX]; char prop_value[PROPERTY_VALUE_MAX] = {'\0'}; - char daemon_cmd[PROPERTY_VALUE_MAX * 2]; + char daemon_cmd[PROPERTY_VALUE_MAX * 2 + sizeof(DHCP_CONFIG_PATH)]; const char *ctrl_prop = "ctl.start"; const char *desired_status = "running"; /* Interface name after converting p2p0-p2p0-X to p2p to reuse system properties */ @@ -206,10 +207,11 @@ int dhcp_do_request(const char *interface, /* Start the daemon and wait until it's ready */ if (property_get(HOSTNAME_PROP_NAME, prop_value, NULL) && (prop_value[0] != '\0')) - snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:-h %s %s", DAEMON_NAME, p2p_interface, - prop_value, interface); + snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:-f %s -h %s %s", DAEMON_NAME, + p2p_interface, DHCP_CONFIG_PATH, prop_value, interface); else - snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:%s", DAEMON_NAME, p2p_interface, interface); + snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:-f %s %s", DAEMON_NAME, + 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) { diff --git a/libnetutils/ifc_utils.c b/libnetutils/ifc_utils.c index f19cb41..eb33d06 100644 --- a/libnetutils/ifc_utils.c +++ b/libnetutils/ifc_utils.c @@ -599,10 +599,6 @@ int ifc_disable(const char *ifname) return result; } -#define RESET_IPV4_ADDRESSES 0x01 -#define RESET_IPV6_ADDRESSES 0x02 -#define RESET_ALL_ADDRESSES (RESET_IPV4_ADDRESSES | RESET_IPV6_ADDRESSES) - int ifc_reset_connections(const char *ifname, const int reset_mask) { #ifdef HAVE_ANDROID_OS diff --git a/libnl_2/Android.mk b/libnl_2/Android.mk index 1745f5a..deac9de 100644 --- a/libnl_2/Android.mk +++ b/libnl_2/Android.mk @@ -1,5 +1,11 @@ +####################################### +# * Netlink cache not implemented +# * Library is not thread safe +####################################### + LOCAL_PATH := $(call my-dir) + include $(CLEAR_VARS) LOCAL_SRC_FILES := \ @@ -22,9 +28,10 @@ LOCAL_MODULE := libnl_2 LOCAL_MODULE_TAGS := optional include $(BUILD_STATIC_LIBRARY) -####################################### -# Shared library currently unavailiable -# * Netlink cache not implemented -# * Library is not thread safe -####################################### - +include $(CLEAR_VARS) +LOCAL_SRC_FILES := +LOCAL_WHOLE_STATIC_LIBRARIES:= libnl_2 +LOCAL_SHARED_LIBRARIES:= liblog +LOCAL_MODULE := libnl_2 +LOCAL_MODULE_TAGS := optional +include $(BUILD_SHARED_LIBRARY) diff --git a/libnl_2/attr.c b/libnl_2/attr.c index f3a2b58..2ef7590 100644 --- a/libnl_2/attr.c +++ b/libnl_2/attr.c @@ -160,9 +160,31 @@ int nla_put(struct nl_msg *msg, int attrtype, int datalen, const void *data) } return -EINVAL; +} + +/* Add 8 bit integer attribute to netlink message. */ +int nla_put_u8(struct nl_msg *msg, int attrtype, uint8_t value) +{ + return nla_put(msg, attrtype, sizeof(uint8_t), &value); +} + +/* Add 16 bit integer attribute to netlink message. */ +int nla_put_u16(struct nl_msg *msg, int attrtype, uint16_t value) +{ + return nla_put(msg, attrtype, sizeof(uint16_t), &value); +} +/* Add 32 bit integer attribute to netlink message. */ +int nla_put_u32(struct nl_msg *msg, int attrtype, uint32_t value) +{ + return nla_put(msg, attrtype, sizeof(uint32_t), &value); } +/* Add 64 bit integer attribute to netlink message. */ +int nla_put_u64(struct nl_msg *msg, int attrtype, uint64_t value) +{ + return nla_put(msg, attrtype, sizeof(uint64_t), &value); +} /* Add nested attributes to netlink message. */ /* Takes the attributes found in the nested message and appends them diff --git a/libnl_2/genl/genl.c b/libnl_2/genl/genl.c index 2442993..1a39c6a 100644 --- a/libnl_2/genl/genl.c +++ b/libnl_2/genl/genl.c @@ -20,7 +20,10 @@ #include <unistd.h> #include <stdio.h> #include <sys/time.h> +#include <sys/socket.h> #include <linux/netlink.h> +#include <netlink/genl/ctrl.h> +#include <netlink/genl/family.h> #include "netlink-types.h" /* Get head of attribute data. */ @@ -250,7 +253,6 @@ error: struct genl_family *genl_ctrl_search_by_name(struct nl_cache *cache, \ const char *name) { - /* TODO: When will we release this memory ? */ struct genl_family *gf = (struct genl_family *) \ malloc(sizeof(struct genl_family)); if (!gf) @@ -262,7 +264,7 @@ struct genl_family *genl_ctrl_search_by_name(struct nl_cache *cache, \ /* Overriding cache pointer as family id for now */ gf->gf_id = (uint16_t) ((uint32_t) cache); - strcpy(gf->gf_name, "nl80211"); + strncpy(gf->gf_name, name, GENL_NAMSIZ); return gf; fail: @@ -272,15 +274,29 @@ fail: int genl_ctrl_resolve(struct nl_sock *sk, const char *name) { + struct nl_cache *cache = NULL; + struct genl_family *gf = NULL; + int id = -1; + /* Hack to support wpa_supplicant */ if (strcmp(name, "nlctrl") == 0) return NETLINK_GENERIC; - else { - int errsv = errno; - fprintf(stderr, \ - "Only nlctrl supported by genl_ctrl_resolve!\n"); - return -errsv; + + if (strcmp(name, "nl80211") != 0) { + fprintf(stderr, "%s is not supported\n", name); + return id; } -} + if (!genl_ctrl_alloc_cache(sk, &cache)) { + gf = genl_ctrl_search_by_name(cache, name); + if (gf) + id = genl_family_get_id(gf); + } + + if (gf) + genl_family_put(gf); + if (cache) + nl_cache_free(cache); + return id; +} diff --git a/libnl_2/msg.c b/libnl_2/msg.c index 283da6e..1303e8a 100644 --- a/libnl_2/msg.c +++ b/libnl_2/msg.c @@ -18,6 +18,7 @@ #include <malloc.h> #include <unistd.h> +#include <sys/socket.h> #include <linux/netlink.h> #include "netlink-types.h" diff --git a/libnl_2/socket.c b/libnl_2/socket.c index d906cac..e94eb9e 100644 --- a/libnl_2/socket.c +++ b/libnl_2/socket.c @@ -127,3 +127,15 @@ int nl_socket_get_fd(struct nl_sock *sk) { return sk->s_fd; } + +void nl_socket_set_cb(struct nl_sock *sk, struct nl_cb *cb) +{ + nl_cb_put(sk->s_cb); + sk->s_cb = cb; + nl_cb_get(cb); +} + +struct nl_cb *nl_socket_get_cb(struct nl_sock *sk) +{ + return nl_cb_get(sk->s_cb); +} diff --git a/libpixelflinger/Android.mk b/libpixelflinger/Android.mk index 1947c2d..488003f 100644 --- a/libpixelflinger/Android.mk +++ b/libpixelflinger/Android.mk @@ -44,6 +44,8 @@ PIXELFLINGER_CFLAGS += -fstrict-aliasing -fomit-frame-pointer endif ifeq ($(TARGET_ARCH),mips) +PIXELFLINGER_SRC_FILES += codeflinger/MIPSAssembler.cpp +PIXELFLINGER_SRC_FILES += codeflinger/mips_disassem.c PIXELFLINGER_SRC_FILES += arch-mips/t32cb16blend.S PIXELFLINGER_CFLAGS += -fstrict-aliasing -fomit-frame-pointer endif diff --git a/libpixelflinger/codeflinger/ARMAssembler.cpp b/libpixelflinger/codeflinger/ARMAssembler.cpp index 0dc5037..c4f42f5 100644 --- a/libpixelflinger/codeflinger/ARMAssembler.cpp +++ b/libpixelflinger/codeflinger/ARMAssembler.cpp @@ -76,6 +76,11 @@ void ARMAssembler::reset() mComments.clear(); } +int ARMAssembler::getCodegenArch() +{ + return CODEGEN_ARCH_ARM; +} + // ---------------------------------------------------------------------------- void ARMAssembler::disassemble(const char* name) @@ -444,5 +449,146 @@ void ARMAssembler::UBFX(int cc, int Rd, int Rn, int lsb, int width) *mPC++ = (cc<<28) | 0x7E00000 | ((width-1)<<16) | (Rd<<12) | (lsb<<7) | 0x50 | Rn; } +#if 0 +#pragma mark - +#pragma mark Addressing modes... +#endif + +int ARMAssembler::buildImmediate( + uint32_t immediate, uint32_t& rot, uint32_t& imm) +{ + rot = 0; + imm = immediate; + if (imm > 0x7F) { // skip the easy cases + while (!(imm&3) || (imm&0xFC000000)) { + uint32_t newval; + newval = imm >> 2; + newval |= (imm&3) << 30; + imm = newval; + rot += 2; + if (rot == 32) { + rot = 0; + break; + } + } + } + rot = (16 - (rot>>1)) & 0xF; + + if (imm>=0x100) + return -EINVAL; + + if (((imm>>(rot<<1)) | (imm<<(32-(rot<<1)))) != immediate) + return -1; + + return 0; +} + +// shifters... + +bool ARMAssembler::isValidImmediate(uint32_t immediate) +{ + uint32_t rot, imm; + return buildImmediate(immediate, rot, imm) == 0; +} + +uint32_t ARMAssembler::imm(uint32_t immediate) +{ + uint32_t rot, imm; + int err = buildImmediate(immediate, rot, imm); + + LOG_ALWAYS_FATAL_IF(err==-EINVAL, + "immediate %08x cannot be encoded", + immediate); + + LOG_ALWAYS_FATAL_IF(err, + "immediate (%08x) encoding bogus!", + immediate); + + return (1<<25) | (rot<<8) | imm; +} + +uint32_t ARMAssembler::reg_imm(int Rm, int type, uint32_t shift) +{ + return ((shift&0x1F)<<7) | ((type&0x3)<<5) | (Rm&0xF); +} + +uint32_t ARMAssembler::reg_rrx(int Rm) +{ + return (ROR<<5) | (Rm&0xF); +} + +uint32_t ARMAssembler::reg_reg(int Rm, int type, int Rs) +{ + return ((Rs&0xF)<<8) | ((type&0x3)<<5) | (1<<4) | (Rm&0xF); +} + +// addressing modes... +// LDR(B)/STR(B)/PLD (immediate and Rm can be negative, which indicate U=0) +uint32_t ARMAssembler::immed12_pre(int32_t immed12, int W) +{ + LOG_ALWAYS_FATAL_IF(abs(immed12) >= 0x800, + "LDR(B)/STR(B)/PLD immediate too big (%08x)", + immed12); + return (1<<24) | (((uint32_t(immed12)>>31)^1)<<23) | + ((W&1)<<21) | (abs(immed12)&0x7FF); +} + +uint32_t ARMAssembler::immed12_post(int32_t immed12) +{ + LOG_ALWAYS_FATAL_IF(abs(immed12) >= 0x800, + "LDR(B)/STR(B)/PLD immediate too big (%08x)", + immed12); + + return (((uint32_t(immed12)>>31)^1)<<23) | (abs(immed12)&0x7FF); +} + +uint32_t ARMAssembler::reg_scale_pre(int Rm, int type, + uint32_t shift, int W) +{ + return (1<<25) | (1<<24) | + (((uint32_t(Rm)>>31)^1)<<23) | ((W&1)<<21) | + reg_imm(abs(Rm), type, shift); +} + +uint32_t ARMAssembler::reg_scale_post(int Rm, int type, uint32_t shift) +{ + return (1<<25) | (((uint32_t(Rm)>>31)^1)<<23) | reg_imm(abs(Rm), type, shift); +} + +// LDRH/LDRSB/LDRSH/STRH (immediate and Rm can be negative, which indicate U=0) +uint32_t ARMAssembler::immed8_pre(int32_t immed8, int W) +{ + uint32_t offset = abs(immed8); + + LOG_ALWAYS_FATAL_IF(abs(immed8) >= 0x100, + "LDRH/LDRSB/LDRSH/STRH immediate too big (%08x)", + immed8); + + return (1<<24) | (1<<22) | (((uint32_t(immed8)>>31)^1)<<23) | + ((W&1)<<21) | (((offset&0xF0)<<4)|(offset&0xF)); +} + +uint32_t ARMAssembler::immed8_post(int32_t immed8) +{ + uint32_t offset = abs(immed8); + + LOG_ALWAYS_FATAL_IF(abs(immed8) >= 0x100, + "LDRH/LDRSB/LDRSH/STRH immediate too big (%08x)", + immed8); + + return (1<<22) | (((uint32_t(immed8)>>31)^1)<<23) | + (((offset&0xF0)<<4) | (offset&0xF)); +} + +uint32_t ARMAssembler::reg_pre(int Rm, int W) +{ + return (1<<24) | (((uint32_t(Rm)>>31)^1)<<23) | ((W&1)<<21) | (abs(Rm)&0xF); +} + +uint32_t ARMAssembler::reg_post(int Rm) +{ + return (((uint32_t(Rm)>>31)^1)<<23) | (abs(Rm)&0xF); +} + }; // namespace android diff --git a/libpixelflinger/codeflinger/ARMAssembler.h b/libpixelflinger/codeflinger/ARMAssembler.h index e7f038a..06c66dd 100644 --- a/libpixelflinger/codeflinger/ARMAssembler.h +++ b/libpixelflinger/codeflinger/ARMAssembler.h @@ -52,11 +52,42 @@ public: virtual void reset(); virtual int generate(const char* name); + virtual int getCodegenArch(); virtual void prolog(); virtual void epilog(uint32_t touched); virtual void comment(const char* string); + + // ----------------------------------------------------------------------- + // shifters and addressing modes + // ----------------------------------------------------------------------- + + // shifters... + virtual bool isValidImmediate(uint32_t immed); + virtual int buildImmediate(uint32_t i, uint32_t& rot, uint32_t& imm); + + virtual uint32_t imm(uint32_t immediate); + virtual uint32_t reg_imm(int Rm, int type, uint32_t shift); + virtual uint32_t reg_rrx(int Rm); + virtual uint32_t reg_reg(int Rm, int type, int Rs); + + // addressing modes... + // LDR(B)/STR(B)/PLD + // (immediate and Rm can be negative, which indicates U=0) + virtual uint32_t immed12_pre(int32_t immed12, int W=0); + virtual uint32_t immed12_post(int32_t immed12); + virtual uint32_t reg_scale_pre(int Rm, int type=0, uint32_t shift=0, int W=0); + virtual uint32_t reg_scale_post(int Rm, int type=0, uint32_t shift=0); + + // LDRH/LDRSB/LDRSH/STRH + // (immediate and Rm can be negative, which indicates U=0) + virtual uint32_t immed8_pre(int32_t immed8, int W=0); + virtual uint32_t immed8_post(int32_t immed8); + virtual uint32_t reg_pre(int Rm, int W=0); + virtual uint32_t reg_post(int Rm); + + virtual void dataProcessing(int opcode, int cc, int s, int Rd, int Rn, uint32_t Op2); @@ -83,21 +114,23 @@ public: virtual uint32_t* pcForLabel(const char* label); virtual void LDR (int cc, int Rd, - int Rn, uint32_t offset = immed12_pre(0)); + int Rn, uint32_t offset = __immed12_pre(0)); virtual void LDRB(int cc, int Rd, - int Rn, uint32_t offset = immed12_pre(0)); + int Rn, uint32_t offset = __immed12_pre(0)); virtual void STR (int cc, int Rd, - int Rn, uint32_t offset = immed12_pre(0)); + int Rn, uint32_t offset = __immed12_pre(0)); virtual void STRB(int cc, int Rd, - int Rn, uint32_t offset = immed12_pre(0)); + int Rn, uint32_t offset = __immed12_pre(0)); virtual void LDRH (int cc, int Rd, - int Rn, uint32_t offset = immed8_pre(0)); + int Rn, uint32_t offset = __immed8_pre(0)); virtual void LDRSB(int cc, int Rd, - int Rn, uint32_t offset = immed8_pre(0)); + int Rn, uint32_t offset = __immed8_pre(0)); virtual void LDRSH(int cc, int Rd, - int Rn, uint32_t offset = immed8_pre(0)); + int Rn, uint32_t offset = __immed8_pre(0)); virtual void STRH (int cc, int Rd, - int Rn, uint32_t offset = immed8_pre(0)); + int Rn, uint32_t offset = __immed8_pre(0)); + + virtual void LDM(int cc, int dir, int Rn, int W, uint32_t reg_list); virtual void STM(int cc, int dir, diff --git a/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp b/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp index 7fa0de0..82180ee 100644 --- a/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp +++ b/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp @@ -32,77 +32,15 @@ ARMAssemblerInterface::~ARMAssemblerInterface() { } -int ARMAssemblerInterface::buildImmediate( - uint32_t immediate, uint32_t& rot, uint32_t& imm) -{ - rot = 0; - imm = immediate; - if (imm > 0x7F) { // skip the easy cases - while (!(imm&3) || (imm&0xFC000000)) { - uint32_t newval; - newval = imm >> 2; - newval |= (imm&3) << 30; - imm = newval; - rot += 2; - if (rot == 32) { - rot = 0; - break; - } - } - } - rot = (16 - (rot>>1)) & 0xF; - - if (imm>=0x100) - return -EINVAL; - - if (((imm>>(rot<<1)) | (imm<<(32-(rot<<1)))) != immediate) - return -1; - - return 0; -} - -// shifters... - -bool ARMAssemblerInterface::isValidImmediate(uint32_t immediate) -{ - uint32_t rot, imm; - return buildImmediate(immediate, rot, imm) == 0; -} - -uint32_t ARMAssemblerInterface::imm(uint32_t immediate) -{ - uint32_t rot, imm; - int err = buildImmediate(immediate, rot, imm); - - LOG_ALWAYS_FATAL_IF(err==-EINVAL, - "immediate %08x cannot be encoded", - immediate); - - LOG_ALWAYS_FATAL_IF(err, - "immediate (%08x) encoding bogus!", - immediate); +// -------------------------------------------------------------------- - return (1<<25) | (rot<<8) | imm; -} - -uint32_t ARMAssemblerInterface::reg_imm(int Rm, int type, uint32_t shift) -{ - return ((shift&0x1F)<<7) | ((type&0x3)<<5) | (Rm&0xF); -} - -uint32_t ARMAssemblerInterface::reg_rrx(int Rm) -{ - return (ROR<<5) | (Rm&0xF); -} +// The following two functions are static and used for initializers +// in the original ARM code. The above versions (without __), are now +// virtual, and can be overridden in the MIPS code. But since these are +// needed at initialization time, they must be static. Not thrilled with +// this implementation, but it works... -uint32_t ARMAssemblerInterface::reg_reg(int Rm, int type, int Rs) -{ - return ((Rs&0xF)<<8) | ((type&0x3)<<5) | (1<<4) | (Rm&0xF); -} - -// addressing modes... -// LDR(B)/STR(B)/PLD (immediate and Rm can be negative, which indicate U=0) -uint32_t ARMAssemblerInterface::immed12_pre(int32_t immed12, int W) +uint32_t ARMAssemblerInterface::__immed12_pre(int32_t immed12, int W) { LOG_ALWAYS_FATAL_IF(abs(immed12) >= 0x800, "LDR(B)/STR(B)/PLD immediate too big (%08x)", @@ -111,30 +49,7 @@ uint32_t ARMAssemblerInterface::immed12_pre(int32_t immed12, int W) ((W&1)<<21) | (abs(immed12)&0x7FF); } -uint32_t ARMAssemblerInterface::immed12_post(int32_t immed12) -{ - LOG_ALWAYS_FATAL_IF(abs(immed12) >= 0x800, - "LDR(B)/STR(B)/PLD immediate too big (%08x)", - immed12); - - return (((uint32_t(immed12)>>31)^1)<<23) | (abs(immed12)&0x7FF); -} - -uint32_t ARMAssemblerInterface::reg_scale_pre(int Rm, int type, - uint32_t shift, int W) -{ - return (1<<25) | (1<<24) | - (((uint32_t(Rm)>>31)^1)<<23) | ((W&1)<<21) | - reg_imm(abs(Rm), type, shift); -} - -uint32_t ARMAssemblerInterface::reg_scale_post(int Rm, int type, uint32_t shift) -{ - return (1<<25) | (((uint32_t(Rm)>>31)^1)<<23) | reg_imm(abs(Rm), type, shift); -} - -// LDRH/LDRSB/LDRSH/STRH (immediate and Rm can be negative, which indicate U=0) -uint32_t ARMAssemblerInterface::immed8_pre(int32_t immed8, int W) +uint32_t ARMAssemblerInterface::__immed8_pre(int32_t immed8, int W) { uint32_t offset = abs(immed8); @@ -146,28 +61,6 @@ uint32_t ARMAssemblerInterface::immed8_pre(int32_t immed8, int W) ((W&1)<<21) | (((offset&0xF0)<<4)|(offset&0xF)); } -uint32_t ARMAssemblerInterface::immed8_post(int32_t immed8) -{ - uint32_t offset = abs(immed8); - - LOG_ALWAYS_FATAL_IF(abs(immed8) >= 0x100, - "LDRH/LDRSB/LDRSH/STRH immediate too big (%08x)", - immed8); - - return (1<<22) | (((uint32_t(immed8)>>31)^1)<<23) | - (((offset&0xF0)<<4) | (offset&0xF)); -} - -uint32_t ARMAssemblerInterface::reg_pre(int Rm, int W) -{ - return (1<<24) | (((uint32_t(Rm)>>31)^1)<<23) | ((W&1)<<21) | (abs(Rm)&0xF); -} - -uint32_t ARMAssemblerInterface::reg_post(int Rm) -{ - return (((uint32_t(Rm)>>31)^1)<<23) | (abs(Rm)&0xF); -} - }; // namespace android diff --git a/libpixelflinger/codeflinger/ARMAssemblerInterface.h b/libpixelflinger/codeflinger/ARMAssemblerInterface.h index 796342a..9991980 100644 --- a/libpixelflinger/codeflinger/ARMAssemblerInterface.h +++ b/libpixelflinger/codeflinger/ARMAssemblerInterface.h @@ -62,33 +62,40 @@ public: LSAVED = LR4|LR5|LR6|LR7|LR8|LR9|LR10|LR11 | LLR }; + enum { + CODEGEN_ARCH_ARM = 1, CODEGEN_ARCH_MIPS + }; + // ----------------------------------------------------------------------- // shifters and addressing modes // ----------------------------------------------------------------------- - // shifters... - static bool isValidImmediate(uint32_t immed); - static int buildImmediate(uint32_t i, uint32_t& rot, uint32_t& imm); + // these static versions are used for initializers on LDxx/STxx below + static uint32_t __immed12_pre(int32_t immed12, int W=0); + static uint32_t __immed8_pre(int32_t immed12, int W=0); + + virtual bool isValidImmediate(uint32_t immed) = 0; + virtual int buildImmediate(uint32_t i, uint32_t& rot, uint32_t& imm) = 0; - static uint32_t imm(uint32_t immediate); - static uint32_t reg_imm(int Rm, int type, uint32_t shift); - static uint32_t reg_rrx(int Rm); - static uint32_t reg_reg(int Rm, int type, int Rs); + virtual uint32_t imm(uint32_t immediate) = 0; + virtual uint32_t reg_imm(int Rm, int type, uint32_t shift) = 0; + virtual uint32_t reg_rrx(int Rm) = 0; + virtual uint32_t reg_reg(int Rm, int type, int Rs) = 0; // addressing modes... // LDR(B)/STR(B)/PLD // (immediate and Rm can be negative, which indicates U=0) - static uint32_t immed12_pre(int32_t immed12, int W=0); - static uint32_t immed12_post(int32_t immed12); - static uint32_t reg_scale_pre(int Rm, int type=0, uint32_t shift=0, int W=0); - static uint32_t reg_scale_post(int Rm, int type=0, uint32_t shift=0); + virtual uint32_t immed12_pre(int32_t immed12, int W=0) = 0; + virtual uint32_t immed12_post(int32_t immed12) = 0; + virtual uint32_t reg_scale_pre(int Rm, int type=0, uint32_t shift=0, int W=0) = 0; + virtual uint32_t reg_scale_post(int Rm, int type=0, uint32_t shift=0) = 0; // LDRH/LDRSB/LDRSH/STRH // (immediate and Rm can be negative, which indicates U=0) - static uint32_t immed8_pre(int32_t immed8, int W=0); - static uint32_t immed8_post(int32_t immed8); - static uint32_t reg_pre(int Rm, int W=0); - static uint32_t reg_post(int Rm); + virtual uint32_t immed8_pre(int32_t immed8, int W=0) = 0; + virtual uint32_t immed8_post(int32_t immed8) = 0; + virtual uint32_t reg_pre(int Rm, int W=0) = 0; + virtual uint32_t reg_post(int Rm) = 0; // ----------------------------------------------------------------------- // basic instructions & code generation @@ -98,6 +105,7 @@ public: virtual void reset() = 0; virtual int generate(const char* name) = 0; virtual void disassemble(const char* name) = 0; + virtual int getCodegenArch() = 0; // construct prolog and epilog virtual void prolog() = 0; @@ -143,22 +151,22 @@ public: // data transfer... virtual void LDR (int cc, int Rd, - int Rn, uint32_t offset = immed12_pre(0)) = 0; + int Rn, uint32_t offset = __immed12_pre(0)) = 0; virtual void LDRB(int cc, int Rd, - int Rn, uint32_t offset = immed12_pre(0)) = 0; + int Rn, uint32_t offset = __immed12_pre(0)) = 0; virtual void STR (int cc, int Rd, - int Rn, uint32_t offset = immed12_pre(0)) = 0; + int Rn, uint32_t offset = __immed12_pre(0)) = 0; virtual void STRB(int cc, int Rd, - int Rn, uint32_t offset = immed12_pre(0)) = 0; + int Rn, uint32_t offset = __immed12_pre(0)) = 0; virtual void LDRH (int cc, int Rd, - int Rn, uint32_t offset = immed8_pre(0)) = 0; + int Rn, uint32_t offset = __immed8_pre(0)) = 0; virtual void LDRSB(int cc, int Rd, - int Rn, uint32_t offset = immed8_pre(0)) = 0; + int Rn, uint32_t offset = __immed8_pre(0)) = 0; virtual void LDRSH(int cc, int Rd, - int Rn, uint32_t offset = immed8_pre(0)) = 0; + int Rn, uint32_t offset = __immed8_pre(0)) = 0; virtual void STRH (int cc, int Rd, - int Rn, uint32_t offset = immed8_pre(0)) = 0; + int Rn, uint32_t offset = __immed8_pre(0)) = 0; // block data transfer... virtual void LDM(int cc, int dir, diff --git a/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp b/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp index c57d7da..7feed62 100644 --- a/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp +++ b/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp @@ -55,6 +55,10 @@ int ARMAssemblerProxy::generate(const char* name) { void ARMAssemblerProxy::disassemble(const char* name) { return mTarget->disassemble(name); } +int ARMAssemblerProxy::getCodegenArch() +{ + return mTarget->getCodegenArch(); +} void ARMAssemblerProxy::prolog() { mTarget->prolog(); } @@ -66,6 +70,93 @@ void ARMAssemblerProxy::comment(const char* string) { } + +// addressing modes + +bool ARMAssemblerProxy::isValidImmediate(uint32_t immed) +{ + return mTarget->isValidImmediate(immed); +} + +int ARMAssemblerProxy::buildImmediate(uint32_t i, uint32_t& rot, uint32_t& imm) +{ + return mTarget->buildImmediate(i, rot, imm); +} + + + +uint32_t ARMAssemblerProxy::imm(uint32_t immediate) +{ + return mTarget->imm(immediate); +} + +uint32_t ARMAssemblerProxy::reg_imm(int Rm, int type, uint32_t shift) +{ + return mTarget->reg_imm(Rm, type, shift); +} + +uint32_t ARMAssemblerProxy::reg_rrx(int Rm) +{ + return mTarget->reg_rrx(Rm); +} + +uint32_t ARMAssemblerProxy::reg_reg(int Rm, int type, int Rs) +{ + return mTarget->reg_reg(Rm, type, Rs); +} + + +// addressing modes... +// LDR(B)/STR(B)/PLD +// (immediate and Rm can be negative, which indicates U=0) +uint32_t ARMAssemblerProxy::immed12_pre(int32_t immed12, int W) +{ + return mTarget->immed12_pre(immed12, W); +} + +uint32_t ARMAssemblerProxy::immed12_post(int32_t immed12) +{ + return mTarget->immed12_post(immed12); +} + +uint32_t ARMAssemblerProxy::reg_scale_pre(int Rm, int type, uint32_t shift, int W) +{ + return mTarget->reg_scale_pre(Rm, type, shift, W); +} + +uint32_t ARMAssemblerProxy::reg_scale_post(int Rm, int type, uint32_t shift) +{ + return mTarget->reg_scale_post(Rm, type, shift); +} + + +// LDRH/LDRSB/LDRSH/STRH +// (immediate and Rm can be negative, which indicates U=0) +uint32_t ARMAssemblerProxy::immed8_pre(int32_t immed8, int W) +{ + return mTarget->immed8_pre(immed8, W); +} + +uint32_t ARMAssemblerProxy::immed8_post(int32_t immed8) +{ + return mTarget->immed8_post(immed8); +} + +uint32_t ARMAssemblerProxy::reg_pre(int Rm, int W) +{ + return mTarget->reg_pre(Rm, W); +} + +uint32_t ARMAssemblerProxy::reg_post(int Rm) +{ + return mTarget->reg_post(Rm); +} + + +//------------------------------------------------------------------------ + + + void ARMAssemblerProxy::dataProcessing( int opcode, int cc, int s, int Rd, int Rn, uint32_t Op2) { diff --git a/libpixelflinger/codeflinger/ARMAssemblerProxy.h b/libpixelflinger/codeflinger/ARMAssemblerProxy.h index 8c7f270..5e3f763 100644 --- a/libpixelflinger/codeflinger/ARMAssemblerProxy.h +++ b/libpixelflinger/codeflinger/ARMAssemblerProxy.h @@ -42,11 +42,40 @@ public: virtual void reset(); virtual int generate(const char* name); virtual void disassemble(const char* name); + virtual int getCodegenArch(); virtual void prolog(); virtual void epilog(uint32_t touched); virtual void comment(const char* string); + // ----------------------------------------------------------------------- + // shifters and addressing modes + // ----------------------------------------------------------------------- + + virtual bool isValidImmediate(uint32_t immed); + virtual int buildImmediate(uint32_t i, uint32_t& rot, uint32_t& imm); + + virtual uint32_t imm(uint32_t immediate); + virtual uint32_t reg_imm(int Rm, int type, uint32_t shift); + virtual uint32_t reg_rrx(int Rm); + virtual uint32_t reg_reg(int Rm, int type, int Rs); + + // addressing modes... + // LDR(B)/STR(B)/PLD + // (immediate and Rm can be negative, which indicates U=0) + virtual uint32_t immed12_pre(int32_t immed12, int W=0); + virtual uint32_t immed12_post(int32_t immed12); + virtual uint32_t reg_scale_pre(int Rm, int type=0, uint32_t shift=0, int W=0); + virtual uint32_t reg_scale_post(int Rm, int type=0, uint32_t shift=0); + + // LDRH/LDRSB/LDRSH/STRH + // (immediate and Rm can be negative, which indicates U=0) + virtual uint32_t immed8_pre(int32_t immed8, int W=0); + virtual uint32_t immed8_post(int32_t immed8); + virtual uint32_t reg_pre(int Rm, int W=0); + virtual uint32_t reg_post(int Rm); + + virtual void dataProcessing(int opcode, int cc, int s, int Rd, int Rn, uint32_t Op2); @@ -73,21 +102,21 @@ public: uint32_t* pcForLabel(const char* label); virtual void LDR (int cc, int Rd, - int Rn, uint32_t offset = immed12_pre(0)); + int Rn, uint32_t offset = __immed12_pre(0)); virtual void LDRB(int cc, int Rd, - int Rn, uint32_t offset = immed12_pre(0)); + int Rn, uint32_t offset = __immed12_pre(0)); virtual void STR (int cc, int Rd, - int Rn, uint32_t offset = immed12_pre(0)); + int Rn, uint32_t offset = __immed12_pre(0)); virtual void STRB(int cc, int Rd, - int Rn, uint32_t offset = immed12_pre(0)); + int Rn, uint32_t offset = __immed12_pre(0)); virtual void LDRH (int cc, int Rd, - int Rn, uint32_t offset = immed8_pre(0)); + int Rn, uint32_t offset = __immed8_pre(0)); virtual void LDRSB(int cc, int Rd, - int Rn, uint32_t offset = immed8_pre(0)); + int Rn, uint32_t offset = __immed8_pre(0)); virtual void LDRSH(int cc, int Rd, - int Rn, uint32_t offset = immed8_pre(0)); + int Rn, uint32_t offset = __immed8_pre(0)); virtual void STRH (int cc, int Rd, - int Rn, uint32_t offset = immed8_pre(0)); + int Rn, uint32_t offset = __immed8_pre(0)); virtual void LDM(int cc, int dir, int Rn, int W, uint32_t reg_list); virtual void STM(int cc, int dir, diff --git a/libpixelflinger/codeflinger/CodeCache.cpp b/libpixelflinger/codeflinger/CodeCache.cpp index 60fc771..f9ae00a 100644 --- a/libpixelflinger/codeflinger/CodeCache.cpp +++ b/libpixelflinger/codeflinger/CodeCache.cpp @@ -22,13 +22,13 @@ #include <unistd.h> #include <sys/mman.h> -#include <cutils/log.h> #include <cutils/ashmem.h> #include <cutils/atomic.h> +#define LOG_TAG "CodeCache" +#include <cutils/log.h> -#include "codeflinger/CodeCache.h" -#define LOG_TAG "CodeCache" +#include "codeflinger/CodeCache.h" namespace android { @@ -39,6 +39,12 @@ namespace android { #include <errno.h> #endif +#if defined(__mips__) +#include <asm/cachectl.h> +#include <errno.h> +#endif + +// ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- // A dlmalloc mspace is used to manage the code cache over a mmaped region. @@ -57,12 +63,7 @@ static void heap_error(const char* msg, const char* function, void* p); #define USAGE_ERROR_ACTION(m,p) \ heap_error("ARGUMENT IS INVALID HEAP ADDRESS", __FUNCTION__, p) - -#pragma GCC diagnostic ignored "-Wstrict-aliasing" -#pragma GCC diagnostic ignored "-Wempty-body" #include "../../../../bionic/libc/upstream-dlmalloc/malloc.c" -#pragma GCC diagnostic warning "-Wstrict-aliasing" -#pragma GCC diagnostic warning "-Wempty-body" static void heap_error(const char* msg, const char* function, void* p) { ALOG(LOG_FATAL, LOG_TAG, "@@@ ABORTING: CODE FLINGER: %s IN %s addr=%p", @@ -200,12 +201,12 @@ int CodeCache::cache( const AssemblyKeyBase& keyBase, mCacheInUse += assemblySize; mWhen++; // synchronize caches... -#if defined(__arm__) +#if defined(__arm__) || defined(__mips__) const long base = long(assembly->base()); const long curr = base + long(assembly->size()); err = cacheflush(base, curr, 0); - ALOGE_IF(err, "__ARM_NR_cacheflush error %s\n", - strerror(errno)); + ALOGE_IF(err, "cacheflush error %s\n", + strerror(errno)); #endif } diff --git a/libpixelflinger/codeflinger/GGLAssembler.cpp b/libpixelflinger/codeflinger/GGLAssembler.cpp index f1d81b2..1ddf93d 100644 --- a/libpixelflinger/codeflinger/GGLAssembler.cpp +++ b/libpixelflinger/codeflinger/GGLAssembler.cpp @@ -31,7 +31,8 @@ namespace android { // ---------------------------------------------------------------------------- GGLAssembler::GGLAssembler(ARMAssemblerInterface* target) - : ARMAssemblerProxy(target), RegisterAllocator(), mOptLevel(7) + : ARMAssemblerProxy(target), + RegisterAllocator(ARMAssemblerProxy::getCodegenArch()), mOptLevel(7) { } @@ -230,7 +231,9 @@ int GGLAssembler::scanline_core(const needs_t& needs, context_t const* c) // texel generation build_textures(parts, regs); - } + if (registerFile().status()) + return registerFile().status(); + } if ((blending & (FACTOR_DST|BLEND_DST)) || (mMasking && !mAllMasked) || @@ -890,6 +893,15 @@ void GGLAssembler::build_and_immediate(int d, int s, uint32_t mask, int bits) return; } + if (getCodegenArch() == CODEGEN_ARCH_MIPS) { + // MIPS can do 16-bit imm in 1 instr, 32-bit in 3 instr + // the below ' while (mask)' code is buggy on mips + // since mips returns true on isValidImmediate() + // then we get multiple AND instr (positive logic) + AND( AL, 0, d, s, imm(mask) ); + return; + } + int negative_logic = !isValidImmediate(mask); if (negative_logic) { mask = ~mask & size; @@ -1002,6 +1014,15 @@ void GGLAssembler::base_offset( // cheezy register allocator... // ---------------------------------------------------------------------------- +// Modified to support MIPS processors, in a very simple way. We retain the +// (Arm) limit of 16 total registers, but shift the mapping of those registers +// from 0-15, to 2-17. Register 0 on Mips cannot be used as GP registers, and +// register 1 has a traditional use as a temp). + +RegisterAllocator::RegisterAllocator(int arch) : mRegs(arch) +{ +} + void RegisterAllocator::reset() { mRegs.reset(); @@ -1029,16 +1050,22 @@ RegisterAllocator::RegisterFile& RegisterAllocator::registerFile() // ---------------------------------------------------------------------------- -RegisterAllocator::RegisterFile::RegisterFile() - : mRegs(0), mTouched(0), mStatus(0) +RegisterAllocator::RegisterFile::RegisterFile(int codegen_arch) + : mRegs(0), mTouched(0), mStatus(0), mArch(codegen_arch), mRegisterOffset(0) { + if (mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS) { + mRegisterOffset = 2; // ARM has regs 0..15, MIPS offset to 2..17 + } reserve(ARMAssemblerInterface::SP); reserve(ARMAssemblerInterface::PC); } -RegisterAllocator::RegisterFile::RegisterFile(const RegisterFile& rhs) - : mRegs(rhs.mRegs), mTouched(rhs.mTouched) +RegisterAllocator::RegisterFile::RegisterFile(const RegisterFile& rhs, int codegen_arch) + : mRegs(rhs.mRegs), mTouched(rhs.mTouched), mArch(codegen_arch), mRegisterOffset(0) { + if (mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS) { + mRegisterOffset = 2; // ARM has regs 0..15, MIPS offset to 2..17 + } } RegisterAllocator::RegisterFile::~RegisterFile() @@ -1057,8 +1084,12 @@ void RegisterAllocator::RegisterFile::reset() reserve(ARMAssemblerInterface::PC); } +// RegisterFile::reserve() take a register parameter in the +// range 0-15 (Arm compatible), but on a Mips processor, will +// return the actual allocated register in the range 2-17. int RegisterAllocator::RegisterFile::reserve(int reg) { + reg += mRegisterOffset; LOG_ALWAYS_FATAL_IF(isUsed(reg), "reserving register %d, but already in use", reg); @@ -1067,6 +1098,7 @@ int RegisterAllocator::RegisterFile::reserve(int reg) return reg; } +// This interface uses regMask in range 2-17 on MIPS, no translation. void RegisterAllocator::RegisterFile::reserveSeveral(uint32_t regMask) { mRegs |= regMask; @@ -1075,7 +1107,7 @@ void RegisterAllocator::RegisterFile::reserveSeveral(uint32_t regMask) int RegisterAllocator::RegisterFile::isUsed(int reg) const { - LOG_ALWAYS_FATAL_IF(reg>=16, "invalid register %d", reg); + LOG_ALWAYS_FATAL_IF(reg>=16+(int)mRegisterOffset, "invalid register %d", reg); return mRegs & (1<<reg); } @@ -1086,10 +1118,10 @@ int RegisterAllocator::RegisterFile::obtain() 6, 7, 8, 9, 10, 11 }; const int nbreg = sizeof(priorityList); - int i, r; + int i, r, reg; for (i=0 ; i<nbreg ; i++) { r = priorityList[i]; - if (!isUsed(r)) { + if (!isUsed(r + mRegisterOffset)) { break; } } @@ -1102,18 +1134,20 @@ int RegisterAllocator::RegisterFile::obtain() // the code will never be run anyway. return ARMAssemblerInterface::SP; } - reserve(r); - return r; + reg = reserve(r); // Param in Arm range 0-15, returns range 2-17 on Mips. + return reg; } bool RegisterAllocator::RegisterFile::hasFreeRegs() const { - return ((mRegs & 0xFFFF) == 0xFFFF) ? false : true; + uint32_t regs = mRegs >> mRegisterOffset; // MIPS fix. + return ((regs & 0xFFFF) == 0xFFFF) ? false : true; } int RegisterAllocator::RegisterFile::countFreeRegs() const { - int f = ~mRegs & 0xFFFF; + uint32_t regs = mRegs >> mRegisterOffset; // MIPS fix. + int f = ~regs & 0xFFFF; // now count number of 1 f = (f & 0x5555) + ((f>>1) & 0x5555); f = (f & 0x3333) + ((f>>2) & 0x3333); @@ -1124,18 +1158,24 @@ int RegisterAllocator::RegisterFile::countFreeRegs() const void RegisterAllocator::RegisterFile::recycle(int reg) { - LOG_FATAL_IF(!isUsed(reg), - "recycling unallocated register %d", - reg); + // commented out, since common failure of running out of regs + // triggers this assertion. Since the code is not execectued + // in that case, it does not matter. No reason to FATAL err. + // LOG_FATAL_IF(!isUsed(reg), + // "recycling unallocated register %d", + // reg); mRegs &= ~(1<<reg); } void RegisterAllocator::RegisterFile::recycleSeveral(uint32_t regMask) { - LOG_FATAL_IF((mRegs & regMask)!=regMask, - "recycling unallocated registers " - "(recycle=%08x, allocated=%08x, unallocated=%08x)", - regMask, mRegs, mRegs®Mask); + // commented out, since common failure of running out of regs + // triggers this assertion. Since the code is not execectued + // in that case, it does not matter. No reason to FATAL err. + // LOG_FATAL_IF((mRegs & regMask)!=regMask, + // "recycling unallocated registers " + // "(recycle=%08x, allocated=%08x, unallocated=%08x)", + // regMask, mRegs, mRegs®Mask); mRegs &= ~regMask; } diff --git a/libpixelflinger/codeflinger/GGLAssembler.h b/libpixelflinger/codeflinger/GGLAssembler.h index d1d29f0..dd5f48e 100644 --- a/libpixelflinger/codeflinger/GGLAssembler.h +++ b/libpixelflinger/codeflinger/GGLAssembler.h @@ -43,6 +43,7 @@ class RegisterAllocator public: class RegisterFile; + RegisterAllocator(int arch); RegisterFile& registerFile(); int reserveReg(int reg); int obtainReg(); @@ -52,8 +53,8 @@ public: class RegisterFile { public: - RegisterFile(); - RegisterFile(const RegisterFile& rhs); + RegisterFile(int arch); + RegisterFile(const RegisterFile& rhs, int arch); ~RegisterFile(); void reset(); @@ -86,6 +87,9 @@ public: uint32_t mRegs; uint32_t mTouched; uint32_t mStatus; + int mArch; + uint32_t mRegisterOffset; // lets reg alloc use 2..17 for mips + // while arm uses 0..15 }; class Scratch diff --git a/libpixelflinger/codeflinger/MIPSAssembler.cpp b/libpixelflinger/codeflinger/MIPSAssembler.cpp new file mode 100644 index 0000000..7888a0e --- /dev/null +++ b/libpixelflinger/codeflinger/MIPSAssembler.cpp @@ -0,0 +1,1957 @@ +/* libs/pixelflinger/codeflinger/MIPSAssembler.cpp +** +** Copyright 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. +*/ + + +/* MIPS assembler and ARM->MIPS assembly translator +** +** The approach is to leave the GGLAssembler and associated files largely +** un-changed, still utilizing all Arm instruction generation. Via the +** ArmToMipsAssembler (subclassed from ArmAssemblerInterface) each Arm +** instruction is translated to one or more Mips instructions as necessary. This +** is clearly less efficient than a direct implementation within the +** GGLAssembler, but is far cleaner, more maintainable, and has yielded very +** significant performance gains on Mips compared to the generic pixel pipeline. +** +** +** GGLAssembler changes +** +** - The register allocator has been modified to re-map Arm registers 0-15 to mips +** registers 2-17. Mips register 0 cannot be used as general-purpose register, +** and register 1 has traditional uses as a short-term temporary. +** +** - Added some early bailouts for OUT_OF_REGISTERS in texturing.cpp and +** GGLAssembler.cpp, since this is not fatal, and can be retried at lower +** optimization level. +** +** +** ARMAssembler and ARMAssemblerInterface changes +** +** Refactored ARM address-mode static functions (imm(), reg_imm(), imm12_pre(), etc.) +** to virtual, so they can be overridden in MIPSAssembler. The implementation of these +** functions on ARM is moved from ARMAssemblerInterface.cpp to ARMAssembler.cpp, and +** is unchanged from the original. (This required duplicating 2 of these as static +** functions in ARMAssemblerInterface.cpp so they could be used as static initializers). +*/ + + +#define LOG_TAG "MIPSAssembler" + +#include <stdio.h> +#include <stdlib.h> +#include <cutils/log.h> +#include <cutils/properties.h> + +#if defined(WITH_LIB_HARDWARE) +#include <hardware_legacy/qemu_tracing.h> +#endif + +#include <private/pixelflinger/ggl_context.h> + +#include "codeflinger/MIPSAssembler.h" +#include "codeflinger/CodeCache.h" +#include "codeflinger/mips_disassem.h" + +// Choose MIPS arch variant following gcc flags +#if defined(__mips__) && __mips==32 && __mips_isa_rev>=2 +#define mips32r2 1 +#else +#define mips32r2 0 +#endif + + +#define NOT_IMPLEMENTED() LOG_ALWAYS_FATAL("Arm instruction %s not yet implemented\n", __func__) + + + +// ---------------------------------------------------------------------------- + +namespace android { + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark ArmToMipsAssembler... +#endif + +ArmToMipsAssembler::ArmToMipsAssembler(const sp<Assembly>& assembly, + char *abuf, int linesz, int instr_count) + : ARMAssemblerInterface(), + mArmDisassemblyBuffer(abuf), + mArmLineLength(linesz), + mArmInstrCount(instr_count), + mInum(0), + mAssembly(assembly) +{ + mMips = new MIPSAssembler(assembly, this); + mArmPC = (uint32_t **) malloc(ARM_MAX_INSTUCTIONS * sizeof(uint32_t *)); + init_conditional_labels(); +} + +ArmToMipsAssembler::~ArmToMipsAssembler() +{ + delete mMips; + free((void *) mArmPC); +} + +uint32_t* ArmToMipsAssembler::pc() const +{ + return mMips->pc(); +} + +uint32_t* ArmToMipsAssembler::base() const +{ + return mMips->base(); +} + +void ArmToMipsAssembler::reset() +{ + cond.labelnum = 0; + mInum = 0; + mMips->reset(); +} + +int ArmToMipsAssembler::getCodegenArch() +{ + return CODEGEN_ARCH_MIPS; +} + +void ArmToMipsAssembler::comment(const char* string) +{ + mMips->comment(string); +} + +void ArmToMipsAssembler::label(const char* theLabel) +{ + mMips->label(theLabel); +} + +void ArmToMipsAssembler::disassemble(const char* name) +{ + mMips->disassemble(name); +} + +void ArmToMipsAssembler::init_conditional_labels() +{ + int i; + for (i=0;i<99; ++i) { + sprintf(cond.label[i], "cond_%d", i); + } +} + + + +#if 0 +#pragma mark - +#pragma mark Prolog/Epilog & Generate... +#endif + +void ArmToMipsAssembler::prolog() +{ + mArmPC[mInum++] = pc(); // save starting PC for this instr + + mMips->ADDIU(R_sp, R_sp, -(5 * 4)); + mMips->SW(R_s0, R_sp, 0); + mMips->SW(R_s1, R_sp, 4); + mMips->SW(R_s2, R_sp, 8); + mMips->SW(R_s3, R_sp, 12); + mMips->SW(R_s4, R_sp, 16); + mMips->MOVE(R_v0, R_a0); // move context * passed in a0 to v0 (arm r0) +} + +void ArmToMipsAssembler::epilog(uint32_t touched) +{ + mArmPC[mInum++] = pc(); // save starting PC for this instr + + mMips->LW(R_s0, R_sp, 0); + mMips->LW(R_s1, R_sp, 4); + mMips->LW(R_s2, R_sp, 8); + mMips->LW(R_s3, R_sp, 12); + mMips->LW(R_s4, R_sp, 16); + mMips->ADDIU(R_sp, R_sp, (5 * 4)); + mMips->JR(R_ra); + +} + +int ArmToMipsAssembler::generate(const char* name) +{ + return mMips->generate(name); +} + +uint32_t* ArmToMipsAssembler::pcForLabel(const char* label) +{ + return mMips->pcForLabel(label); +} + + + +//---------------------------------------------------------- + +#if 0 +#pragma mark - +#pragma mark Addressing modes & shifters... +#endif + + +// do not need this for MIPS, but it is in the Interface (virtual) +int ArmToMipsAssembler::buildImmediate( + uint32_t immediate, uint32_t& rot, uint32_t& imm) +{ + // for MIPS, any 32-bit immediate is OK + rot = 0; + imm = immediate; + return 0; +} + +// shifters... + +bool ArmToMipsAssembler::isValidImmediate(uint32_t immediate) +{ + // for MIPS, any 32-bit immediate is OK + return true; +} + +uint32_t ArmToMipsAssembler::imm(uint32_t immediate) +{ + // ALOGW("immediate value %08x at pc %08x\n", immediate, (int)pc()); + amode.value = immediate; + return AMODE_IMM; +} + +uint32_t ArmToMipsAssembler::reg_imm(int Rm, int type, uint32_t shift) +{ + amode.reg = Rm; + amode.stype = type; + amode.value = shift; + return AMODE_REG_IMM; +} + +uint32_t ArmToMipsAssembler::reg_rrx(int Rm) +{ + // reg_rrx mode is not used in the GLLAssember code at this time + return AMODE_UNSUPPORTED; +} + +uint32_t ArmToMipsAssembler::reg_reg(int Rm, int type, int Rs) +{ + // reg_reg mode is not used in the GLLAssember code at this time + return AMODE_UNSUPPORTED; +} + + +// addressing modes... +// LDR(B)/STR(B)/PLD (immediate and Rm can be negative, which indicate U=0) +uint32_t ArmToMipsAssembler::immed12_pre(int32_t immed12, int W) +{ + LOG_ALWAYS_FATAL_IF(abs(immed12) >= 0x800, + "LDR(B)/STR(B)/PLD immediate too big (%08x)", + immed12); + amode.value = immed12; + amode.writeback = W; + return AMODE_IMM_12_PRE; +} + +uint32_t ArmToMipsAssembler::immed12_post(int32_t immed12) +{ + LOG_ALWAYS_FATAL_IF(abs(immed12) >= 0x800, + "LDR(B)/STR(B)/PLD immediate too big (%08x)", + immed12); + + amode.value = immed12; + return AMODE_IMM_12_POST; +} + +uint32_t ArmToMipsAssembler::reg_scale_pre(int Rm, int type, + uint32_t shift, int W) +{ + LOG_ALWAYS_FATAL_IF(W | type | shift, "reg_scale_pre adv modes not yet implemented"); + + amode.reg = Rm; + // amode.stype = type; // more advanced modes not used in GGLAssembler yet + // amode.value = shift; + // amode.writeback = W; + return AMODE_REG_SCALE_PRE; +} + +uint32_t ArmToMipsAssembler::reg_scale_post(int Rm, int type, uint32_t shift) +{ + LOG_ALWAYS_FATAL("adr mode reg_scale_post not yet implemented\n"); + return AMODE_UNSUPPORTED; +} + +// LDRH/LDRSB/LDRSH/STRH (immediate and Rm can be negative, which indicate U=0) +uint32_t ArmToMipsAssembler::immed8_pre(int32_t immed8, int W) +{ + // uint32_t offset = abs(immed8); + + LOG_ALWAYS_FATAL("adr mode immed8_pre not yet implemented\n"); + + LOG_ALWAYS_FATAL_IF(abs(immed8) >= 0x100, + "LDRH/LDRSB/LDRSH/STRH immediate too big (%08x)", + immed8); + return AMODE_IMM_8_PRE; +} + +uint32_t ArmToMipsAssembler::immed8_post(int32_t immed8) +{ + // uint32_t offset = abs(immed8); + + LOG_ALWAYS_FATAL_IF(abs(immed8) >= 0x100, + "LDRH/LDRSB/LDRSH/STRH immediate too big (%08x)", + immed8); + amode.value = immed8; + return AMODE_IMM_8_POST; +} + +uint32_t ArmToMipsAssembler::reg_pre(int Rm, int W) +{ + LOG_ALWAYS_FATAL_IF(W, "reg_pre writeback not yet implemented"); + amode.reg = Rm; + return AMODE_REG_PRE; +} + +uint32_t ArmToMipsAssembler::reg_post(int Rm) +{ + LOG_ALWAYS_FATAL("adr mode reg_post not yet implemented\n"); + return AMODE_UNSUPPORTED; +} + + + +// ---------------------------------------------------------------------------- + +#if 0 +#pragma mark - +#pragma mark Data Processing... +#endif + + +static const char * const dpOpNames[] = { + "AND", "EOR", "SUB", "RSB", "ADD", "ADC", "SBC", "RSC", + "TST", "TEQ", "CMP", "CMN", "ORR", "MOV", "BIC", "MVN" +}; + +// check if the operand registers from a previous CMP or S-bit instruction +// would be overwritten by this instruction. If so, move the value to a +// safe register. +// Note that we cannot tell at _this_ instruction time if a future (conditional) +// instruction will _also_ use this value (a defect of the simple 1-pass, one- +// instruction-at-a-time translation). Therefore we must be conservative and +// save the value before it is overwritten. This costs an extra MOVE instr. + +void ArmToMipsAssembler::protectConditionalOperands(int Rd) +{ + if (Rd == cond.r1) { + mMips->MOVE(R_cmp, cond.r1); + cond.r1 = R_cmp; + } + if (cond.type == CMP_COND && Rd == cond.r2) { + mMips->MOVE(R_cmp2, cond.r2); + cond.r2 = R_cmp2; + } +} + + +// interprets the addressing mode, and generates the common code +// used by the majority of data-processing ops. Many MIPS instructions +// have a register-based form and a different immediate form. See +// opAND below for an example. (this could be inlined) +// +// this works with the imm(), reg_imm() methods above, which are directly +// called by the GLLAssembler. +// note: _signed parameter defaults to false (un-signed) +// note: tmpReg parameter defaults to 1, MIPS register AT +int ArmToMipsAssembler::dataProcAdrModes(int op, int& source, bool _signed, int tmpReg) +{ + if (op < AMODE_REG) { + source = op; + return SRC_REG; + } else if (op == AMODE_IMM) { + if ((!_signed && amode.value > 0xffff) + || (_signed && ((int)amode.value < -32768 || (int)amode.value > 32767) )) { + mMips->LUI(tmpReg, (amode.value >> 16)); + if (amode.value & 0x0000ffff) { + mMips->ORI(tmpReg, tmpReg, (amode.value & 0x0000ffff)); + } + source = tmpReg; + return SRC_REG; + } else { + source = amode.value; + return SRC_IMM; + } + } else if (op == AMODE_REG_IMM) { + switch (amode.stype) { + case LSL: mMips->SLL(tmpReg, amode.reg, amode.value); break; + case LSR: mMips->SRL(tmpReg, amode.reg, amode.value); break; + case ASR: mMips->SRA(tmpReg, amode.reg, amode.value); break; + case ROR: if (mips32r2) { + mMips->ROTR(tmpReg, amode.reg, amode.value); + } else { + mMips->RORIsyn(tmpReg, amode.reg, amode.value); + } + break; + } + source = tmpReg; + return SRC_REG; + } else { // adr mode RRX is not used in GGL Assembler at this time + // we are screwed, this should be exception, assert-fail or something + LOG_ALWAYS_FATAL("adr mode reg_rrx not yet implemented\n"); + return SRC_ERROR; + } +} + + +void ArmToMipsAssembler::dataProcessing(int opcode, int cc, + int s, int Rd, int Rn, uint32_t Op2) +{ + int src; // src is modified by dataProcAdrModes() - passed as int& + + + if (cc != AL) { + protectConditionalOperands(Rd); + // the branch tests register(s) set by prev CMP or instr with 'S' bit set + // inverse the condition to jump past this conditional instruction + ArmToMipsAssembler::B(cc^1, cond.label[++cond.labelnum]); + } else { + mArmPC[mInum++] = pc(); // save starting PC for this instr + } + + switch (opcode) { + case opAND: + if (dataProcAdrModes(Op2, src) == SRC_REG) { + mMips->AND(Rd, Rn, src); + } else { // adr mode was SRC_IMM + mMips->ANDI(Rd, Rn, src); + } + break; + + case opADD: + // set "signed" to true for adr modes + if (dataProcAdrModes(Op2, src, true) == SRC_REG) { + mMips->ADDU(Rd, Rn, src); + } else { // adr mode was SRC_IMM + mMips->ADDIU(Rd, Rn, src); + } + break; + + case opSUB: + // set "signed" to true for adr modes + if (dataProcAdrModes(Op2, src, true) == SRC_REG) { + mMips->SUBU(Rd, Rn, src); + } else { // adr mode was SRC_IMM + mMips->SUBIU(Rd, Rn, src); + } + break; + + case opEOR: + if (dataProcAdrModes(Op2, src) == SRC_REG) { + mMips->XOR(Rd, Rn, src); + } else { // adr mode was SRC_IMM + mMips->XORI(Rd, Rn, src); + } + break; + + case opORR: + if (dataProcAdrModes(Op2, src) == SRC_REG) { + mMips->OR(Rd, Rn, src); + } else { // adr mode was SRC_IMM + mMips->ORI(Rd, Rn, src); + } + break; + + case opBIC: + if (dataProcAdrModes(Op2, src) == SRC_IMM) { + // if we are 16-bit imnmediate, load to AT reg + mMips->ORI(R_at, 0, src); + src = R_at; + } + mMips->NOT(R_at, src); + mMips->AND(Rd, Rn, R_at); + break; + + case opRSB: + if (dataProcAdrModes(Op2, src) == SRC_IMM) { + // if we are 16-bit imnmediate, load to AT reg + mMips->ORI(R_at, 0, src); + src = R_at; + } + mMips->SUBU(Rd, src, Rn); // subu with the parameters reversed + break; + + case opMOV: + if (Op2 < AMODE_REG) { // op2 is reg # in this case + mMips->MOVE(Rd, Op2); + } else if (Op2 == AMODE_IMM) { + if (amode.value > 0xffff) { + mMips->LUI(Rd, (amode.value >> 16)); + if (amode.value & 0x0000ffff) { + mMips->ORI(Rd, Rd, (amode.value & 0x0000ffff)); + } + } else { + mMips->ORI(Rd, 0, amode.value); + } + } else if (Op2 == AMODE_REG_IMM) { + switch (amode.stype) { + case LSL: mMips->SLL(Rd, amode.reg, amode.value); break; + case LSR: mMips->SRL(Rd, amode.reg, amode.value); break; + case ASR: mMips->SRA(Rd, amode.reg, amode.value); break; + case ROR: if (mips32r2) { + mMips->ROTR(Rd, amode.reg, amode.value); + } else { + mMips->RORIsyn(Rd, amode.reg, amode.value); + } + break; + } + } + else { + // adr mode RRX is not used in GGL Assembler at this time + mMips->UNIMPL(); + } + break; + + case opMVN: // this is a 1's complement: NOT + if (Op2 < AMODE_REG) { // op2 is reg # in this case + mMips->NOR(Rd, Op2, 0); // NOT is NOR with 0 + break; + } else if (Op2 == AMODE_IMM) { + if (amode.value > 0xffff) { + mMips->LUI(Rd, (amode.value >> 16)); + if (amode.value & 0x0000ffff) { + mMips->ORI(Rd, Rd, (amode.value & 0x0000ffff)); + } + } else { + mMips->ORI(Rd, 0, amode.value); + } + } else if (Op2 == AMODE_REG_IMM) { + switch (amode.stype) { + case LSL: mMips->SLL(Rd, amode.reg, amode.value); break; + case LSR: mMips->SRL(Rd, amode.reg, amode.value); break; + case ASR: mMips->SRA(Rd, amode.reg, amode.value); break; + case ROR: if (mips32r2) { + mMips->ROTR(Rd, amode.reg, amode.value); + } else { + mMips->RORIsyn(Rd, amode.reg, amode.value); + } + break; + } + } + else { + // adr mode RRX is not used in GGL Assembler at this time + mMips->UNIMPL(); + } + mMips->NOR(Rd, Rd, 0); // NOT is NOR with 0 + break; + + case opCMP: + // Either operand of a CMP instr could get overwritten by a subsequent + // conditional instruction, which is ok, _UNLESS_ there is a _second_ + // conditional instruction. Under MIPS, this requires doing the comparison + // again (SLT), and the original operands must be available. (and this + // pattern of multiple conditional instructions from same CMP _is_ used + // in GGL-Assembler) + // + // For now, if a conditional instr overwrites the operands, we will + // move them to dedicated temp regs. This is ugly, and inefficient, + // and should be optimized. + // + // WARNING: making an _Assumption_ that CMP operand regs will NOT be + // trashed by intervening NON-conditional instructions. In the general + // case this is legal, but it is NOT currently done in GGL-Assembler. + + cond.type = CMP_COND; + cond.r1 = Rn; + if (dataProcAdrModes(Op2, src, false, R_cmp2) == SRC_REG) { + cond.r2 = src; + } else { // adr mode was SRC_IMM + mMips->ORI(R_cmp2, R_zero, src); + cond.r2 = R_cmp2; + } + + break; + + + case opTST: + case opTEQ: + case opCMN: + case opADC: + case opSBC: + case opRSC: + mMips->UNIMPL(); // currently unused in GGL Assembler code + break; + } + + if (cc != AL) { + mMips->label(cond.label[cond.labelnum]); + } + if (s && opcode != opCMP) { + cond.type = SBIT_COND; + cond.r1 = Rd; + } +} + + + +#if 0 +#pragma mark - +#pragma mark Multiply... +#endif + +// multiply, accumulate +void ArmToMipsAssembler::MLA(int cc, int s, + int Rd, int Rm, int Rs, int Rn) { + + mArmPC[mInum++] = pc(); // save starting PC for this instr + + mMips->MUL(R_at, Rm, Rs); + mMips->ADDU(Rd, R_at, Rn); + if (s) { + cond.type = SBIT_COND; + cond.r1 = Rd; + } +} + +void ArmToMipsAssembler::MUL(int cc, int s, + int Rd, int Rm, int Rs) { + mArmPC[mInum++] = pc(); + mMips->MUL(Rd, Rm, Rs); + if (s) { + cond.type = SBIT_COND; + cond.r1 = Rd; + } +} + +void ArmToMipsAssembler::UMULL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs) { + mArmPC[mInum++] = pc(); + mMips->MULT(Rm, Rs); + mMips->MFHI(RdHi); + mMips->MFLO(RdLo); + if (s) { + cond.type = SBIT_COND; + cond.r1 = RdHi; // BUG... + LOG_ALWAYS_FATAL("Condition on UMULL must be on 64-bit result\n"); + } +} + +void ArmToMipsAssembler::UMUAL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs) { + LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi, + "UMUAL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs); + // *mPC++ = (cc<<28) | (1<<23) | (1<<21) | (s<<20) | + // (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); + if (s) { + cond.type = SBIT_COND; + cond.r1 = RdHi; // BUG... + LOG_ALWAYS_FATAL("Condition on UMULL must be on 64-bit result\n"); + } +} + +void ArmToMipsAssembler::SMULL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs) { + LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi, + "SMULL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs); + // *mPC++ = (cc<<28) | (1<<23) | (1<<22) | (s<<20) | + // (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); + if (s) { + cond.type = SBIT_COND; + cond.r1 = RdHi; // BUG... + LOG_ALWAYS_FATAL("Condition on SMULL must be on 64-bit result\n"); + } +} +void ArmToMipsAssembler::SMUAL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs) { + LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi, + "SMUAL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs); + // *mPC++ = (cc<<28) | (1<<23) | (1<<22) | (1<<21) | (s<<20) | + // (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); + if (s) { + cond.type = SBIT_COND; + cond.r1 = RdHi; // BUG... + LOG_ALWAYS_FATAL("Condition on SMUAL must be on 64-bit result\n"); + } +} + + + +#if 0 +#pragma mark - +#pragma mark Branches... +#endif + +// branches... + +void ArmToMipsAssembler::B(int cc, const char* label) +{ + mArmPC[mInum++] = pc(); + if (cond.type == SBIT_COND) { cond.r2 = R_zero; } + + switch(cc) { + case EQ: mMips->BEQ(cond.r1, cond.r2, label); break; + case NE: mMips->BNE(cond.r1, cond.r2, label); break; + case HS: mMips->BGEU(cond.r1, cond.r2, label); break; + case LO: mMips->BLTU(cond.r1, cond.r2, label); break; + case MI: mMips->BLT(cond.r1, cond.r2, label); break; + case PL: mMips->BGE(cond.r1, cond.r2, label); break; + + case HI: mMips->BGTU(cond.r1, cond.r2, label); break; + case LS: mMips->BLEU(cond.r1, cond.r2, label); break; + case GE: mMips->BGE(cond.r1, cond.r2, label); break; + case LT: mMips->BLT(cond.r1, cond.r2, label); break; + case GT: mMips->BGT(cond.r1, cond.r2, label); break; + case LE: mMips->BLE(cond.r1, cond.r2, label); break; + case AL: mMips->B(label); break; + case NV: /* B Never - no instruction */ break; + + case VS: + case VC: + default: + LOG_ALWAYS_FATAL("Unsupported cc: %02x\n", cc); + break; + } +} + +void ArmToMipsAssembler::BL(int cc, const char* label) +{ + LOG_ALWAYS_FATAL("branch-and-link not supported yet\n"); + mArmPC[mInum++] = pc(); +} + +// no use for Branches with integer PC, but they're in the Interface class .... +void ArmToMipsAssembler::B(int cc, uint32_t* to_pc) +{ + LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n"); + mArmPC[mInum++] = pc(); +} + +void ArmToMipsAssembler::BL(int cc, uint32_t* to_pc) +{ + LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n"); + mArmPC[mInum++] = pc(); +} + +void ArmToMipsAssembler::BX(int cc, int Rn) +{ + LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n"); + mArmPC[mInum++] = pc(); +} + + + +#if 0 +#pragma mark - +#pragma mark Data Transfer... +#endif + +// data transfer... +void ArmToMipsAssembler::LDR(int cc, int Rd, int Rn, uint32_t offset) +{ + mArmPC[mInum++] = pc(); + // work-around for ARM default address mode of immed12_pre(0) + if (offset > AMODE_UNSUPPORTED) offset = 0; + switch (offset) { + case 0: + amode.value = 0; + amode.writeback = 0; + // fall thru to next case .... + case AMODE_IMM_12_PRE: + if (Rn == ARMAssemblerInterface::SP) { + Rn = R_sp; // convert LDR via Arm SP to LW via Mips SP + } + mMips->LW(Rd, Rn, amode.value); + if (amode.writeback) { // OPTIONAL writeback on pre-index mode + mMips->ADDIU(Rn, Rn, amode.value); + } + break; + case AMODE_IMM_12_POST: + if (Rn == ARMAssemblerInterface::SP) { + Rn = R_sp; // convert STR thru Arm SP to STR thru Mips SP + } + mMips->LW(Rd, Rn, 0); + mMips->ADDIU(Rn, Rn, amode.value); + break; + case AMODE_REG_SCALE_PRE: + // we only support simple base + index, no advanced modes for this one yet + mMips->ADDU(R_at, Rn, amode.reg); + mMips->LW(Rd, R_at, 0); + break; + } +} + +void ArmToMipsAssembler::LDRB(int cc, int Rd, int Rn, uint32_t offset) +{ + mArmPC[mInum++] = pc(); + // work-around for ARM default address mode of immed12_pre(0) + if (offset > AMODE_UNSUPPORTED) offset = 0; + switch (offset) { + case 0: + amode.value = 0; + amode.writeback = 0; + // fall thru to next case .... + case AMODE_IMM_12_PRE: + mMips->LBU(Rd, Rn, amode.value); + if (amode.writeback) { // OPTIONAL writeback on pre-index mode + mMips->ADDIU(Rn, Rn, amode.value); + } + break; + case AMODE_IMM_12_POST: + mMips->LBU(Rd, Rn, 0); + mMips->ADDIU(Rn, Rn, amode.value); + break; + case AMODE_REG_SCALE_PRE: + // we only support simple base + index, no advanced modes for this one yet + mMips->ADDU(R_at, Rn, amode.reg); + mMips->LBU(Rd, R_at, 0); + break; + } + +} + +void ArmToMipsAssembler::STR(int cc, int Rd, int Rn, uint32_t offset) +{ + mArmPC[mInum++] = pc(); + // work-around for ARM default address mode of immed12_pre(0) + if (offset > AMODE_UNSUPPORTED) offset = 0; + switch (offset) { + case 0: + amode.value = 0; + amode.writeback = 0; + // fall thru to next case .... + case AMODE_IMM_12_PRE: + if (Rn == ARMAssemblerInterface::SP) { + Rn = R_sp; // convert STR thru Arm SP to SW thru Mips SP + } + if (amode.writeback) { // OPTIONAL writeback on pre-index mode + // If we will writeback, then update the index reg, then store. + // This correctly handles stack-push case. + mMips->ADDIU(Rn, Rn, amode.value); + mMips->SW(Rd, Rn, 0); + } else { + // No writeback so store offset by value + mMips->SW(Rd, Rn, amode.value); + } + break; + case AMODE_IMM_12_POST: + mMips->SW(Rd, Rn, 0); + mMips->ADDIU(Rn, Rn, amode.value); // post index always writes back + break; + case AMODE_REG_SCALE_PRE: + // we only support simple base + index, no advanced modes for this one yet + mMips->ADDU(R_at, Rn, amode.reg); + mMips->SW(Rd, R_at, 0); + break; + } +} + +void ArmToMipsAssembler::STRB(int cc, int Rd, int Rn, uint32_t offset) +{ + mArmPC[mInum++] = pc(); + // work-around for ARM default address mode of immed12_pre(0) + if (offset > AMODE_UNSUPPORTED) offset = 0; + switch (offset) { + case 0: + amode.value = 0; + amode.writeback = 0; + // fall thru to next case .... + case AMODE_IMM_12_PRE: + mMips->SB(Rd, Rn, amode.value); + if (amode.writeback) { // OPTIONAL writeback on pre-index mode + mMips->ADDIU(Rn, Rn, amode.value); + } + break; + case AMODE_IMM_12_POST: + mMips->SB(Rd, Rn, 0); + mMips->ADDIU(Rn, Rn, amode.value); + break; + case AMODE_REG_SCALE_PRE: + // we only support simple base + index, no advanced modes for this one yet + mMips->ADDU(R_at, Rn, amode.reg); + mMips->SB(Rd, R_at, 0); + break; + } +} + +void ArmToMipsAssembler::LDRH(int cc, int Rd, int Rn, uint32_t offset) +{ + mArmPC[mInum++] = pc(); + // work-around for ARM default address mode of immed8_pre(0) + if (offset > AMODE_UNSUPPORTED) offset = 0; + switch (offset) { + case 0: + amode.value = 0; + // fall thru to next case .... + case AMODE_IMM_8_PRE: // no support yet for writeback + mMips->LHU(Rd, Rn, amode.value); + break; + case AMODE_IMM_8_POST: + mMips->LHU(Rd, Rn, 0); + mMips->ADDIU(Rn, Rn, amode.value); + break; + case AMODE_REG_PRE: + // we only support simple base +/- index + if (amode.reg >= 0) { + mMips->ADDU(R_at, Rn, amode.reg); + } else { + mMips->SUBU(R_at, Rn, abs(amode.reg)); + } + mMips->LHU(Rd, R_at, 0); + break; + } +} + +void ArmToMipsAssembler::LDRSB(int cc, int Rd, int Rn, uint32_t offset) +{ + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +void ArmToMipsAssembler::LDRSH(int cc, int Rd, int Rn, uint32_t offset) +{ + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +void ArmToMipsAssembler::STRH(int cc, int Rd, int Rn, uint32_t offset) +{ + mArmPC[mInum++] = pc(); + // work-around for ARM default address mode of immed8_pre(0) + if (offset > AMODE_UNSUPPORTED) offset = 0; + switch (offset) { + case 0: + amode.value = 0; + // fall thru to next case .... + case AMODE_IMM_8_PRE: // no support yet for writeback + mMips->SH(Rd, Rn, amode.value); + break; + case AMODE_IMM_8_POST: + mMips->SH(Rd, Rn, 0); + mMips->ADDIU(Rn, Rn, amode.value); + break; + case AMODE_REG_PRE: + // we only support simple base +/- index + if (amode.reg >= 0) { + mMips->ADDU(R_at, Rn, amode.reg); + } else { + mMips->SUBU(R_at, Rn, abs(amode.reg)); + } + mMips->SH(Rd, R_at, 0); + break; + } +} + + + +#if 0 +#pragma mark - +#pragma mark Block Data Transfer... +#endif + +// block data transfer... +void ArmToMipsAssembler::LDM(int cc, int dir, + int Rn, int W, uint32_t reg_list) +{ // ED FD EA FA IB IA DB DA + // const uint8_t P[8] = { 1, 0, 1, 0, 1, 0, 1, 0 }; + // const uint8_t U[8] = { 1, 1, 0, 0, 1, 1, 0, 0 }; + // *mPC++ = (cc<<28) | (4<<25) | (uint32_t(P[dir])<<24) | + // (uint32_t(U[dir])<<23) | (1<<20) | (W<<21) | (Rn<<16) | reg_list; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +void ArmToMipsAssembler::STM(int cc, int dir, + int Rn, int W, uint32_t reg_list) +{ // FA EA FD ED IB IA DB DA + // const uint8_t P[8] = { 0, 1, 0, 1, 1, 0, 1, 0 }; + // const uint8_t U[8] = { 0, 0, 1, 1, 1, 1, 0, 0 }; + // *mPC++ = (cc<<28) | (4<<25) | (uint32_t(P[dir])<<24) | + // (uint32_t(U[dir])<<23) | (0<<20) | (W<<21) | (Rn<<16) | reg_list; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + + + +#if 0 +#pragma mark - +#pragma mark Special... +#endif + +// special... +void ArmToMipsAssembler::SWP(int cc, int Rn, int Rd, int Rm) { + // *mPC++ = (cc<<28) | (2<<23) | (Rn<<16) | (Rd << 12) | 0x90 | Rm; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +void ArmToMipsAssembler::SWPB(int cc, int Rn, int Rd, int Rm) { + // *mPC++ = (cc<<28) | (2<<23) | (1<<22) | (Rn<<16) | (Rd << 12) | 0x90 | Rm; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +void ArmToMipsAssembler::SWI(int cc, uint32_t comment) { + // *mPC++ = (cc<<28) | (0xF<<24) | comment; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + + +#if 0 +#pragma mark - +#pragma mark DSP instructions... +#endif + +// DSP instructions... +void ArmToMipsAssembler::PLD(int Rn, uint32_t offset) { + LOG_ALWAYS_FATAL_IF(!((offset&(1<<24)) && !(offset&(1<<21))), + "PLD only P=1, W=0"); + // *mPC++ = 0xF550F000 | (Rn<<16) | offset; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +void ArmToMipsAssembler::CLZ(int cc, int Rd, int Rm) +{ + mArmPC[mInum++] = pc(); + mMips->CLZ(Rd, Rm); +} + +void ArmToMipsAssembler::QADD(int cc, int Rd, int Rm, int Rn) +{ + // *mPC++ = (cc<<28) | 0x1000050 | (Rn<<16) | (Rd<<12) | Rm; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +void ArmToMipsAssembler::QDADD(int cc, int Rd, int Rm, int Rn) +{ + // *mPC++ = (cc<<28) | 0x1400050 | (Rn<<16) | (Rd<<12) | Rm; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +void ArmToMipsAssembler::QSUB(int cc, int Rd, int Rm, int Rn) +{ + // *mPC++ = (cc<<28) | 0x1200050 | (Rn<<16) | (Rd<<12) | Rm; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +void ArmToMipsAssembler::QDSUB(int cc, int Rd, int Rm, int Rn) +{ + // *mPC++ = (cc<<28) | 0x1600050 | (Rn<<16) | (Rd<<12) | Rm; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +// 16 x 16 signed multiply (like SMLAxx without the accumulate) +void ArmToMipsAssembler::SMUL(int cc, int xy, + int Rd, int Rm, int Rs) +{ + mArmPC[mInum++] = pc(); + + // the 16 bits may be in the top or bottom half of 32-bit source reg, + // as defined by the codes BB, BT, TB, TT (compressed param xy) + // where x corresponds to Rm and y to Rs + + // select half-reg for Rm + if (xy & xyTB) { + // use top 16-bits + mMips->SRA(R_at, Rm, 16); + } else { + // use bottom 16, but sign-extend to 32 + if (mips32r2) { + mMips->SEH(R_at, Rm); + } else { + mMips->SLL(R_at, Rm, 16); + mMips->SRA(R_at, R_at, 16); + } + } + // select half-reg for Rs + if (xy & xyBT) { + // use top 16-bits + mMips->SRA(R_at2, Rs, 16); + } else { + // use bottom 16, but sign-extend to 32 + if (mips32r2) { + mMips->SEH(R_at2, Rs); + } else { + mMips->SLL(R_at2, Rs, 16); + mMips->SRA(R_at2, R_at2, 16); + } + } + mMips->MUL(Rd, R_at, R_at2); +} + +// signed 32b x 16b multiple, save top 32-bits of 48-bit result +void ArmToMipsAssembler::SMULW(int cc, int y, + int Rd, int Rm, int Rs) +{ + mArmPC[mInum++] = pc(); + + // the selector yT or yB refers to reg Rs + if (y & yT) { + // zero the bottom 16-bits, with 2 shifts, it can affect result + mMips->SRL(R_at, Rs, 16); + mMips->SLL(R_at, R_at, 16); + + } else { + // move low 16-bit half, to high half + mMips->SLL(R_at, Rs, 16); + } + mMips->MULT(Rm, R_at); + mMips->MFHI(Rd); +} + +// 16 x 16 signed multiply, accumulate: Rd = Rm{16} * Rs{16} + Rn +void ArmToMipsAssembler::SMLA(int cc, int xy, + int Rd, int Rm, int Rs, int Rn) +{ + mArmPC[mInum++] = pc(); + + // the 16 bits may be in the top or bottom half of 32-bit source reg, + // as defined by the codes BB, BT, TB, TT (compressed param xy) + // where x corresponds to Rm and y to Rs + + // select half-reg for Rm + if (xy & xyTB) { + // use top 16-bits + mMips->SRA(R_at, Rm, 16); + } else { + // use bottom 16, but sign-extend to 32 + if (mips32r2) { + mMips->SEH(R_at, Rm); + } else { + mMips->SLL(R_at, Rm, 16); + mMips->SRA(R_at, R_at, 16); + } + } + // select half-reg for Rs + if (xy & xyBT) { + // use top 16-bits + mMips->SRA(R_at2, Rs, 16); + } else { + // use bottom 16, but sign-extend to 32 + if (mips32r2) { + mMips->SEH(R_at2, Rs); + } else { + mMips->SLL(R_at2, Rs, 16); + mMips->SRA(R_at2, R_at2, 16); + } + } + + mMips->MUL(R_at, R_at, R_at2); + mMips->ADDU(Rd, R_at, Rn); +} + +void ArmToMipsAssembler::SMLAL(int cc, int xy, + int RdHi, int RdLo, int Rs, int Rm) +{ + // *mPC++ = (cc<<28) | 0x1400080 | (RdHi<<16) | (RdLo<<12) | (Rs<<8) | (xy<<4) | Rm; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +void ArmToMipsAssembler::SMLAW(int cc, int y, + int Rd, int Rm, int Rs, int Rn) +{ + // *mPC++ = (cc<<28) | 0x1200080 | (Rd<<16) | (Rn<<12) | (Rs<<8) | (y<<4) | Rm; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +// used by ARMv6 version of GGLAssembler::filter32 +void ArmToMipsAssembler::UXTB16(int cc, int Rd, int Rm, int rotate) +{ + mArmPC[mInum++] = pc(); + + //Rd[31:16] := ZeroExtend((Rm ROR (8 * sh))[23:16]), + //Rd[15:0] := ZeroExtend((Rm ROR (8 * sh))[7:0]). sh 0-3. + + mMips->ROTR(Rm, Rm, rotate * 8); + mMips->AND(Rd, Rm, 0x00FF00FF); +} + +void ArmToMipsAssembler::UBFX(int cc, int Rd, int Rn, int lsb, int width) +{ + /* Placeholder for UBFX */ + mArmPC[mInum++] = pc(); + + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + + + + + +#if 0 +#pragma mark - +#pragma mark MIPS Assembler... +#endif + + +//************************************************************************** +//************************************************************************** +//************************************************************************** + + +/* mips assembler +** this is a subset of mips32r2, targeted specifically at ARM instruction +** replacement in the pixelflinger/codeflinger code. +** +** To that end, there is no need for floating point, or priviledged +** instructions. This all runs in user space, no float. +** +** The syntax makes no attempt to be as complete as the assember, with +** synthetic instructions, and automatic recognition of immedate operands +** (use the immediate form of the instruction), etc. +** +** We start with mips32r1, and may add r2 and dsp extensions if cpu +** supports. Decision will be made at compile time, based on gcc +** options. (makes sense since android will be built for a a specific +** device) +*/ + +MIPSAssembler::MIPSAssembler(const sp<Assembly>& assembly, ArmToMipsAssembler *parent) + : mParent(parent), + mAssembly(assembly) +{ + mBase = mPC = (uint32_t *)assembly->base(); + mDuration = ggl_system_time(); +} + +MIPSAssembler::~MIPSAssembler() +{ +} + + +uint32_t* MIPSAssembler::pc() const +{ + return mPC; +} + +uint32_t* MIPSAssembler::base() const +{ + return mBase; +} + +void MIPSAssembler::reset() +{ + mBase = mPC = (uint32_t *)mAssembly->base(); + mBranchTargets.clear(); + mLabels.clear(); + mLabelsInverseMapping.clear(); + mComments.clear(); +} + + +// convert tabs to spaces, and remove any newline +// works with strings of limited size (makes a temp copy) +#define TABSTOP 8 +void MIPSAssembler::string_detab(char *s) +{ + char *os = s; + char temp[100]; + char *t = temp; + int len = 99; + int i = TABSTOP; + + while (*s && len-- > 0) { + if (*s == '\n') { s++; continue; } + if (*s == '\t') { + s++; + for ( ; i>0; i--) {*t++ = ' '; len--; } + } else { + *t++ = *s++; + } + if (i <= 0) i = TABSTOP; + i--; + } + *t = '\0'; + strcpy(os, temp); +} + +void MIPSAssembler::string_pad(char *s, int padded_len) +{ + int len = strlen(s); + s += len; + for (int i = padded_len - len; i > 0; --i) { + *s++ = ' '; + } + *s = '\0'; +} + +// ---------------------------------------------------------------------------- + +void MIPSAssembler::disassemble(const char* name) +{ + char di_buf[140]; + + if (name) { + ALOGW("%s:\n", name); + } + + bool arm_disasm_fmt = (mParent->mArmDisassemblyBuffer == NULL) ? false : true; + + typedef char dstr[40]; + dstr *lines = (dstr *)mParent->mArmDisassemblyBuffer; + + if (mParent->mArmDisassemblyBuffer != NULL) { + for (int i=0; i<mParent->mArmInstrCount; ++i) { + string_detab(lines[i]); + } + } + + // iArm is an index to Arm instructions 1...n for this assembly sequence + // mArmPC[iArm] holds the value of the Mips-PC for the first MIPS + // instruction corresponding to that Arm instruction number + + int iArm = 0; + size_t count = pc()-base(); + uint32_t* mipsPC = base(); + while (count--) { + ssize_t label = mLabelsInverseMapping.indexOfKey(mipsPC); + if (label >= 0) { + ALOGW("%s:\n", mLabelsInverseMapping.valueAt(label)); + } + ssize_t comment = mComments.indexOfKey(mipsPC); + if (comment >= 0) { + ALOGW("; %s\n", mComments.valueAt(comment)); + } + // ALOGW("%08x: %08x ", int(i), int(i[0])); + ::mips_disassem(mipsPC, di_buf, arm_disasm_fmt); + string_detab(di_buf); + string_pad(di_buf, 30); + ALOGW("%08x: %08x %s", uint32_t(mipsPC), uint32_t(*mipsPC), di_buf); + mipsPC++; + } +} + +void MIPSAssembler::comment(const char* string) +{ + mComments.add(pc(), string); +} + +void MIPSAssembler::label(const char* theLabel) +{ + mLabels.add(theLabel, pc()); + mLabelsInverseMapping.add(pc(), theLabel); +} + + +void MIPSAssembler::prolog() +{ + // empty - done in ArmToMipsAssembler +} + +void MIPSAssembler::epilog(uint32_t touched) +{ + // empty - done in ArmToMipsAssembler +} + +int MIPSAssembler::generate(const char* name) +{ + // fixup all the branches + size_t count = mBranchTargets.size(); + while (count--) { + const branch_target_t& bt = mBranchTargets[count]; + uint32_t* target_pc = mLabels.valueFor(bt.label); + LOG_ALWAYS_FATAL_IF(!target_pc, + "error resolving branch targets, target_pc is null"); + int32_t offset = int32_t(target_pc - (bt.pc+1)); + *bt.pc |= offset & 0x00FFFF; + } + + mAssembly->resize( int(pc()-base())*4 ); + + // the instruction & data caches are flushed by CodeCache + const int64_t duration = ggl_system_time() - mDuration; + const char * const format = "generated %s (%d ins) at [%p:%p] in %lld ns\n"; + ALOGI(format, name, int(pc()-base()), base(), pc(), duration); + +#if defined(WITH_LIB_HARDWARE) + if (__builtin_expect(mQemuTracing, 0)) { + int err = qemu_add_mapping(int(base()), name); + mQemuTracing = (err >= 0); + } +#endif + + char value[PROPERTY_VALUE_MAX]; + value[0] = '\0'; + + property_get("debug.pf.disasm", value, "0"); + + if (atoi(value) != 0) { + disassemble(name); + } + + return NO_ERROR; +} + +uint32_t* MIPSAssembler::pcForLabel(const char* label) +{ + return mLabels.valueFor(label); +} + + + +#if 0 +#pragma mark - +#pragma mark Arithmetic... +#endif + +void MIPSAssembler::ADDU(int Rd, int Rs, int Rt) +{ + *mPC++ = (spec_op<<OP_SHF) | (addu_fn<<FUNC_SHF) + | (Rs<<RS_SHF) | (Rt<<RT_SHF) | (Rd<<RD_SHF); +} + +// MD00086 pdf says this is: ADDIU rt, rs, imm -- they do not use Rd +void MIPSAssembler::ADDIU(int Rt, int Rs, int16_t imm) +{ + *mPC++ = (addiu_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | (imm & MSK_16); +} + + +void MIPSAssembler::SUBU(int Rd, int Rs, int Rt) +{ + *mPC++ = (spec_op<<OP_SHF) | (subu_fn<<FUNC_SHF) | + (Rs<<RS_SHF) | (Rt<<RT_SHF) | (Rd<<RD_SHF) ; +} + + +void MIPSAssembler::SUBIU(int Rt, int Rs, int16_t imm) // really addiu(d, s, -j) +{ + *mPC++ = (addiu_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | ((-imm) & MSK_16); +} + + +void MIPSAssembler::NEGU(int Rd, int Rs) // really subu(d, zero, s) +{ + MIPSAssembler::SUBU(Rd, 0, Rs); +} + +void MIPSAssembler::MUL(int Rd, int Rs, int Rt) +{ + *mPC++ = (spec2_op<<OP_SHF) | (mul_fn<<FUNC_SHF) | + (Rs<<RS_SHF) | (Rt<<RT_SHF) | (Rd<<RD_SHF) ; +} + +void MIPSAssembler::MULT(int Rs, int Rt) // dest is hi,lo +{ + *mPC++ = (spec_op<<OP_SHF) | (mult_fn<<FUNC_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF); +} + +void MIPSAssembler::MULTU(int Rs, int Rt) // dest is hi,lo +{ + *mPC++ = (spec_op<<OP_SHF) | (multu_fn<<FUNC_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF); +} + +void MIPSAssembler::MADD(int Rs, int Rt) // hi,lo = hi,lo + Rs * Rt +{ + *mPC++ = (spec2_op<<OP_SHF) | (madd_fn<<FUNC_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF); +} + +void MIPSAssembler::MADDU(int Rs, int Rt) // hi,lo = hi,lo + Rs * Rt +{ + *mPC++ = (spec2_op<<OP_SHF) | (maddu_fn<<FUNC_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF); +} + + +void MIPSAssembler::MSUB(int Rs, int Rt) // hi,lo = hi,lo - Rs * Rt +{ + *mPC++ = (spec2_op<<OP_SHF) | (msub_fn<<FUNC_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF); +} + +void MIPSAssembler::MSUBU(int Rs, int Rt) // hi,lo = hi,lo - Rs * Rt +{ + *mPC++ = (spec2_op<<OP_SHF) | (msubu_fn<<FUNC_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF); +} + + +void MIPSAssembler::SEB(int Rd, int Rt) // sign-extend byte (mips32r2) +{ + *mPC++ = (spec3_op<<OP_SHF) | (bshfl_fn<<FUNC_SHF) | (seb_fn << SA_SHF) | + (Rt<<RT_SHF) | (Rd<<RD_SHF); +} + +void MIPSAssembler::SEH(int Rd, int Rt) // sign-extend half-word (mips32r2) +{ + *mPC++ = (spec3_op<<OP_SHF) | (bshfl_fn<<FUNC_SHF) | (seh_fn << SA_SHF) | + (Rt<<RT_SHF) | (Rd<<RD_SHF); +} + + + +#if 0 +#pragma mark - +#pragma mark Comparisons... +#endif + +void MIPSAssembler::SLT(int Rd, int Rs, int Rt) +{ + *mPC++ = (spec_op<<OP_SHF) | (slt_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF); +} + +void MIPSAssembler::SLTI(int Rt, int Rs, int16_t imm) +{ + *mPC++ = (slti_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | (imm & MSK_16); +} + + +void MIPSAssembler::SLTU(int Rd, int Rs, int Rt) +{ + *mPC++ = (spec_op<<OP_SHF) | (sltu_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF); +} + +void MIPSAssembler::SLTIU(int Rt, int Rs, int16_t imm) +{ + *mPC++ = (sltiu_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | (imm & MSK_16); +} + + + +#if 0 +#pragma mark - +#pragma mark Logical... +#endif + +void MIPSAssembler::AND(int Rd, int Rs, int Rt) +{ + *mPC++ = (spec_op<<OP_SHF) | (and_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF); +} + +void MIPSAssembler::ANDI(int Rt, int Rs, uint16_t imm) // todo: support larger immediate +{ + *mPC++ = (andi_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | (imm & MSK_16); +} + + +void MIPSAssembler::OR(int Rd, int Rs, int Rt) +{ + *mPC++ = (spec_op<<OP_SHF) | (or_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF); +} + +void MIPSAssembler::ORI(int Rt, int Rs, uint16_t imm) +{ + *mPC++ = (ori_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | (imm & MSK_16); +} + +void MIPSAssembler::NOR(int Rd, int Rs, int Rt) +{ + *mPC++ = (spec_op<<OP_SHF) | (nor_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF); +} + +void MIPSAssembler::NOT(int Rd, int Rs) +{ + MIPSAssembler::NOR(Rd, Rs, 0); // NOT(d,s) = NOR(d,s,zero) +} + +void MIPSAssembler::XOR(int Rd, int Rs, int Rt) +{ + *mPC++ = (spec_op<<OP_SHF) | (xor_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF); +} + +void MIPSAssembler::XORI(int Rt, int Rs, uint16_t imm) // todo: support larger immediate +{ + *mPC++ = (xori_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | (imm & MSK_16); +} + +void MIPSAssembler::SLL(int Rd, int Rt, int shft) +{ + *mPC++ = (spec_op<<OP_SHF) | (sll_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rt<<RT_SHF) | (shft<<RE_SHF); +} + +void MIPSAssembler::SLLV(int Rd, int Rt, int Rs) +{ + *mPC++ = (spec_op<<OP_SHF) | (sllv_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF); +} + +void MIPSAssembler::SRL(int Rd, int Rt, int shft) +{ + *mPC++ = (spec_op<<OP_SHF) | (srl_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rt<<RT_SHF) | (shft<<RE_SHF); +} + +void MIPSAssembler::SRLV(int Rd, int Rt, int Rs) +{ + *mPC++ = (spec_op<<OP_SHF) | (srlv_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF); +} + +void MIPSAssembler::SRA(int Rd, int Rt, int shft) +{ + *mPC++ = (spec_op<<OP_SHF) | (sra_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rt<<RT_SHF) | (shft<<RE_SHF); +} + +void MIPSAssembler::SRAV(int Rd, int Rt, int Rs) +{ + *mPC++ = (spec_op<<OP_SHF) | (srav_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF); +} + +void MIPSAssembler::ROTR(int Rd, int Rt, int shft) // mips32r2 +{ + // note weird encoding (SRL + 1) + *mPC++ = (spec_op<<OP_SHF) | (srl_fn<<FUNC_SHF) | + (1<<RS_SHF) | (Rd<<RD_SHF) | (Rt<<RT_SHF) | (shft<<RE_SHF); +} + +void MIPSAssembler::ROTRV(int Rd, int Rt, int Rs) // mips32r2 +{ + // note weird encoding (SRLV + 1) + *mPC++ = (spec_op<<OP_SHF) | (srlv_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF) | (1<<RE_SHF); +} + +// uses at2 register (mapped to some appropriate mips reg) +void MIPSAssembler::RORsyn(int Rd, int Rt, int Rs) +{ + // synthetic: d = t rotated by s + MIPSAssembler::NEGU(R_at2, Rs); + MIPSAssembler::SLLV(R_at2, Rt, R_at2); + MIPSAssembler::SRLV(Rd, Rt, Rs); + MIPSAssembler::OR(Rd, Rd, R_at2); +} + +// immediate version - uses at2 register (mapped to some appropriate mips reg) +void MIPSAssembler::RORIsyn(int Rd, int Rt, int rot) +{ + // synthetic: d = t rotated by immed rot + // d = s >> rot | s << (32-rot) + MIPSAssembler::SLL(R_at2, Rt, 32-rot); + MIPSAssembler::SRL(Rd, Rt, rot); + MIPSAssembler::OR(Rd, Rd, R_at2); +} + +void MIPSAssembler::CLO(int Rd, int Rs) +{ + // Rt field must have same gpr # as Rd + *mPC++ = (spec2_op<<OP_SHF) | (clo_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rd<<RT_SHF); +} + +void MIPSAssembler::CLZ(int Rd, int Rs) +{ + // Rt field must have same gpr # as Rd + *mPC++ = (spec2_op<<OP_SHF) | (clz_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rd<<RT_SHF); +} + +void MIPSAssembler::WSBH(int Rd, int Rt) // mips32r2 +{ + *mPC++ = (spec3_op<<OP_SHF) | (bshfl_fn<<FUNC_SHF) | (wsbh_fn << SA_SHF) | + (Rt<<RT_SHF) | (Rd<<RD_SHF); +} + + + +#if 0 +#pragma mark - +#pragma mark Load/store... +#endif + +void MIPSAssembler::LW(int Rt, int Rbase, int16_t offset) +{ + *mPC++ = (lw_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16); +} + +void MIPSAssembler::SW(int Rt, int Rbase, int16_t offset) +{ + *mPC++ = (sw_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16); +} + +// lb is sign-extended +void MIPSAssembler::LB(int Rt, int Rbase, int16_t offset) +{ + *mPC++ = (lb_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16); +} + +void MIPSAssembler::LBU(int Rt, int Rbase, int16_t offset) +{ + *mPC++ = (lbu_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16); +} + +void MIPSAssembler::SB(int Rt, int Rbase, int16_t offset) +{ + *mPC++ = (sb_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16); +} + +// lh is sign-extended +void MIPSAssembler::LH(int Rt, int Rbase, int16_t offset) +{ + *mPC++ = (lh_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16); +} + +void MIPSAssembler::LHU(int Rt, int Rbase, int16_t offset) +{ + *mPC++ = (lhu_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16); +} + +void MIPSAssembler::SH(int Rt, int Rbase, int16_t offset) +{ + *mPC++ = (sh_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16); +} + +void MIPSAssembler::LUI(int Rt, int16_t offset) +{ + *mPC++ = (lui_op<<OP_SHF) | (Rt<<RT_SHF) | (offset & MSK_16); +} + + + +#if 0 +#pragma mark - +#pragma mark Register move... +#endif + +void MIPSAssembler::MOVE(int Rd, int Rs) +{ + // encoded as "or rd, rs, zero" + *mPC++ = (spec_op<<OP_SHF) | (or_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rs<<RS_SHF) | (0<<RT_SHF); +} + +void MIPSAssembler::MOVN(int Rd, int Rs, int Rt) +{ + *mPC++ = (spec_op<<OP_SHF) | (movn_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF); +} + +void MIPSAssembler::MOVZ(int Rd, int Rs, int Rt) +{ + *mPC++ = (spec_op<<OP_SHF) | (movz_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF); +} + +void MIPSAssembler::MFHI(int Rd) +{ + *mPC++ = (spec_op<<OP_SHF) | (mfhi_fn<<FUNC_SHF) | (Rd<<RD_SHF); +} + +void MIPSAssembler::MFLO(int Rd) +{ + *mPC++ = (spec_op<<OP_SHF) | (mflo_fn<<FUNC_SHF) | (Rd<<RD_SHF); +} + +void MIPSAssembler::MTHI(int Rs) +{ + *mPC++ = (spec_op<<OP_SHF) | (mthi_fn<<FUNC_SHF) | (Rs<<RS_SHF); +} + +void MIPSAssembler::MTLO(int Rs) +{ + *mPC++ = (spec_op<<OP_SHF) | (mtlo_fn<<FUNC_SHF) | (Rs<<RS_SHF); +} + + + +#if 0 +#pragma mark - +#pragma mark Branch... +#endif + +// temporarily forcing a NOP into branch-delay slot, just to be safe +// todo: remove NOP, optimze use of delay slots +void MIPSAssembler::B(const char* label) +{ + mBranchTargets.add(branch_target_t(label, mPC)); + + // encoded as BEQ zero, zero, offset + *mPC++ = (beq_op<<OP_SHF) | (0<<RT_SHF) + | (0<<RS_SHF) | 0; // offset filled in later + + MIPSAssembler::NOP(); +} + +void MIPSAssembler::BEQ(int Rs, int Rt, const char* label) +{ + mBranchTargets.add(branch_target_t(label, mPC)); + *mPC++ = (beq_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | 0; + MIPSAssembler::NOP(); +} + +void MIPSAssembler::BNE(int Rs, int Rt, const char* label) +{ + mBranchTargets.add(branch_target_t(label, mPC)); + *mPC++ = (bne_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | 0; + MIPSAssembler::NOP(); +} + +void MIPSAssembler::BLEZ(int Rs, const char* label) +{ + mBranchTargets.add(branch_target_t(label, mPC)); + *mPC++ = (blez_op<<OP_SHF) | (0<<RT_SHF) | (Rs<<RS_SHF) | 0; + MIPSAssembler::NOP(); +} + +void MIPSAssembler::BLTZ(int Rs, const char* label) +{ + mBranchTargets.add(branch_target_t(label, mPC)); + *mPC++ = (regimm_op<<OP_SHF) | (bltz_fn<<RT_SHF) | (Rs<<RS_SHF) | 0; + MIPSAssembler::NOP(); +} + +void MIPSAssembler::BGTZ(int Rs, const char* label) +{ + mBranchTargets.add(branch_target_t(label, mPC)); + *mPC++ = (bgtz_op<<OP_SHF) | (0<<RT_SHF) | (Rs<<RS_SHF) | 0; + MIPSAssembler::NOP(); +} + + +void MIPSAssembler::BGEZ(int Rs, const char* label) +{ + mBranchTargets.add(branch_target_t(label, mPC)); + *mPC++ = (regimm_op<<OP_SHF) | (bgez_fn<<RT_SHF) | (Rs<<RS_SHF) | 0; + MIPSAssembler::NOP(); +} + +void MIPSAssembler::JR(int Rs) +{ + *mPC++ = (spec_op<<OP_SHF) | (Rs<<RS_SHF) | (jr_fn << FUNC_SHF); + MIPSAssembler::NOP(); +} + + +#if 0 +#pragma mark - +#pragma mark Synthesized Branch... +#endif + +// synthetic variants of branches (using slt & friends) +void MIPSAssembler::BEQZ(int Rs, const char* label) +{ + BEQ(Rs, R_zero, label); +} + +void MIPSAssembler::BNEZ(int Rs, const char* label) +{ + BNE(R_at, R_zero, label); +} + +void MIPSAssembler::BGE(int Rs, int Rt, const char* label) +{ + SLT(R_at, Rs, Rt); + BEQ(R_at, R_zero, label); +} + +void MIPSAssembler::BGEU(int Rs, int Rt, const char* label) +{ + SLTU(R_at, Rs, Rt); + BEQ(R_at, R_zero, label); +} + +void MIPSAssembler::BGT(int Rs, int Rt, const char* label) +{ + SLT(R_at, Rt, Rs); // rev + BNE(R_at, R_zero, label); +} + +void MIPSAssembler::BGTU(int Rs, int Rt, const char* label) +{ + SLTU(R_at, Rt, Rs); // rev + BNE(R_at, R_zero, label); +} + +void MIPSAssembler::BLE(int Rs, int Rt, const char* label) +{ + SLT(R_at, Rt, Rs); // rev + BEQ(R_at, R_zero, label); +} + +void MIPSAssembler::BLEU(int Rs, int Rt, const char* label) +{ + SLTU(R_at, Rt, Rs); // rev + BEQ(R_at, R_zero, label); +} + +void MIPSAssembler::BLT(int Rs, int Rt, const char* label) +{ + SLT(R_at, Rs, Rt); + BNE(R_at, R_zero, label); +} + +void MIPSAssembler::BLTU(int Rs, int Rt, const char* label) +{ + SLTU(R_at, Rs, Rt); + BNE(R_at, R_zero, label); +} + + + + +#if 0 +#pragma mark - +#pragma mark Misc... +#endif + +void MIPSAssembler::NOP(void) +{ + // encoded as "sll zero, zero, 0", which is all zero + *mPC++ = (spec_op<<OP_SHF) | (sll_fn<<FUNC_SHF); +} + +// using this as special opcode for not-yet-implemented ARM instruction +void MIPSAssembler::NOP2(void) +{ + // encoded as "sll zero, zero, 2", still a nop, but a unique code + *mPC++ = (spec_op<<OP_SHF) | (sll_fn<<FUNC_SHF) | (2 << RE_SHF); +} + +// using this as special opcode for purposefully NOT implemented ARM instruction +void MIPSAssembler::UNIMPL(void) +{ + // encoded as "sll zero, zero, 3", still a nop, but a unique code + *mPC++ = (spec_op<<OP_SHF) | (sll_fn<<FUNC_SHF) | (3 << RE_SHF); +} + + +}; // namespace android: + + diff --git a/libpixelflinger/codeflinger/MIPSAssembler.h b/libpixelflinger/codeflinger/MIPSAssembler.h new file mode 100644 index 0000000..d8e8165 --- /dev/null +++ b/libpixelflinger/codeflinger/MIPSAssembler.h @@ -0,0 +1,555 @@ +/* libs/pixelflinger/codeflinger/MIPSAssembler.h +** +** Copyright 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. +*/ + +#ifndef ANDROID_MIPSASSEMBLER_H +#define ANDROID_MIPSASSEMBLER_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Vector.h> +#include <utils/KeyedVector.h> + +#include "tinyutils/smartpointer.h" +#include "codeflinger/ARMAssemblerInterface.h" +#include "codeflinger/CodeCache.h" + +namespace android { + +class MIPSAssembler; // forward reference + +// this class mimics ARMAssembler interface +// intent is to translate each ARM instruction to 1 or more MIPS instr +// implementation calls MIPSAssembler class to generate mips code +class ArmToMipsAssembler : public ARMAssemblerInterface +{ +public: + ArmToMipsAssembler(const sp<Assembly>& assembly, + char *abuf = 0, int linesz = 0, int instr_count = 0); + virtual ~ArmToMipsAssembler(); + + uint32_t* base() const; + uint32_t* pc() const; + void disassemble(const char* name); + + virtual void reset(); + + virtual int generate(const char* name); + virtual int getCodegenArch(); + + virtual void prolog(); + virtual void epilog(uint32_t touched); + virtual void comment(const char* string); + + + // ----------------------------------------------------------------------- + // shifters and addressing modes + // ----------------------------------------------------------------------- + + // shifters... + virtual bool isValidImmediate(uint32_t immed); + virtual int buildImmediate(uint32_t i, uint32_t& rot, uint32_t& imm); + + virtual uint32_t imm(uint32_t immediate); + virtual uint32_t reg_imm(int Rm, int type, uint32_t shift); + virtual uint32_t reg_rrx(int Rm); + virtual uint32_t reg_reg(int Rm, int type, int Rs); + + // addressing modes... + // LDR(B)/STR(B)/PLD + // (immediate and Rm can be negative, which indicates U=0) + virtual uint32_t immed12_pre(int32_t immed12, int W=0); + virtual uint32_t immed12_post(int32_t immed12); + virtual uint32_t reg_scale_pre(int Rm, int type=0, uint32_t shift=0, int W=0); + virtual uint32_t reg_scale_post(int Rm, int type=0, uint32_t shift=0); + + // LDRH/LDRSB/LDRSH/STRH + // (immediate and Rm can be negative, which indicates U=0) + virtual uint32_t immed8_pre(int32_t immed8, int W=0); + virtual uint32_t immed8_post(int32_t immed8); + virtual uint32_t reg_pre(int Rm, int W=0); + virtual uint32_t reg_post(int Rm); + + + + + virtual void dataProcessing(int opcode, int cc, int s, + int Rd, int Rn, + uint32_t Op2); + virtual void MLA(int cc, int s, + int Rd, int Rm, int Rs, int Rn); + virtual void MUL(int cc, int s, + int Rd, int Rm, int Rs); + virtual void UMULL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs); + virtual void UMUAL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs); + virtual void SMULL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs); + virtual void SMUAL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs); + + virtual void B(int cc, uint32_t* pc); + virtual void BL(int cc, uint32_t* pc); + virtual void BX(int cc, int Rn); + virtual void label(const char* theLabel); + virtual void B(int cc, const char* label); + virtual void BL(int cc, const char* label); + + virtual uint32_t* pcForLabel(const char* label); + + virtual void LDR (int cc, int Rd, + int Rn, uint32_t offset = 0); + virtual void LDRB(int cc, int Rd, + int Rn, uint32_t offset = 0); + virtual void STR (int cc, int Rd, + int Rn, uint32_t offset = 0); + virtual void STRB(int cc, int Rd, + int Rn, uint32_t offset = 0); + virtual void LDRH (int cc, int Rd, + int Rn, uint32_t offset = 0); + virtual void LDRSB(int cc, int Rd, + int Rn, uint32_t offset = 0); + virtual void LDRSH(int cc, int Rd, + int Rn, uint32_t offset = 0); + virtual void STRH (int cc, int Rd, + int Rn, uint32_t offset = 0); + + virtual void LDM(int cc, int dir, + int Rn, int W, uint32_t reg_list); + virtual void STM(int cc, int dir, + int Rn, int W, uint32_t reg_list); + + virtual void SWP(int cc, int Rn, int Rd, int Rm); + virtual void SWPB(int cc, int Rn, int Rd, int Rm); + virtual void SWI(int cc, uint32_t comment); + + virtual void PLD(int Rn, uint32_t offset); + virtual void CLZ(int cc, int Rd, int Rm); + virtual void QADD(int cc, int Rd, int Rm, int Rn); + virtual void QDADD(int cc, int Rd, int Rm, int Rn); + virtual void QSUB(int cc, int Rd, int Rm, int Rn); + virtual void QDSUB(int cc, int Rd, int Rm, int Rn); + virtual void SMUL(int cc, int xy, + int Rd, int Rm, int Rs); + virtual void SMULW(int cc, int y, + int Rd, int Rm, int Rs); + virtual void SMLA(int cc, int xy, + int Rd, int Rm, int Rs, int Rn); + virtual void SMLAL(int cc, int xy, + int RdHi, int RdLo, int Rs, int Rm); + virtual void SMLAW(int cc, int y, + int Rd, int Rm, int Rs, int Rn); + + // byte/half word extract... + virtual void UXTB16(int cc, int Rd, int Rm, int rotate); + + // bit manipulation... + virtual void UBFX(int cc, int Rd, int Rn, int lsb, int width); + + // this is some crap to share is MIPSAssembler class for debug + char * mArmDisassemblyBuffer; + int mArmLineLength; + int mArmInstrCount; + + int mInum; // current arm instuction number (0..n) + uint32_t** mArmPC; // array: PC for 1st mips instr of + // each translated ARM instr + + +private: + ArmToMipsAssembler(const ArmToMipsAssembler& rhs); + ArmToMipsAssembler& operator = (const ArmToMipsAssembler& rhs); + + void init_conditional_labels(void); + + void protectConditionalOperands(int Rd); + + // reg__tmp set to MIPS AT, reg 1 + int dataProcAdrModes(int op, int& source, bool sign = false, int reg_tmp = 1); + + sp<Assembly> mAssembly; + MIPSAssembler* mMips; + + + enum misc_constants_t { + ARM_MAX_INSTUCTIONS = 512 // based on ASSEMBLY_SCRATCH_SIZE + }; + + enum { + SRC_REG = 0, + SRC_IMM, + SRC_ERROR = -1 + }; + + enum addr_modes { + // start above the range of legal mips reg #'s (0-31) + AMODE_REG = 0x20, + AMODE_IMM, AMODE_REG_IMM, // for data processing + AMODE_IMM_12_PRE, AMODE_IMM_12_POST, // for load/store + AMODE_REG_SCALE_PRE, AMODE_IMM_8_PRE, + AMODE_IMM_8_POST, AMODE_REG_PRE, + AMODE_UNSUPPORTED + }; + + struct addr_mode_t { // address modes for current ARM instruction + int reg; + int stype; + uint32_t value; + bool writeback; // writeback the adr reg after modification + } amode; + + enum cond_types { + CMP_COND = 1, + SBIT_COND + }; + + struct cond_mode_t { // conditional-execution info for current ARM instruction + cond_types type; + int r1; + int r2; + int labelnum; + char label[100][10]; + } cond; + +}; + + + + +// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- + +// This is the basic MIPS assembler, which just creates the opcodes in memory. +// All the more complicated work is done in ArmToMipsAssember above. + +class MIPSAssembler +{ +public: + MIPSAssembler(const sp<Assembly>& assembly, ArmToMipsAssembler *parent); + virtual ~MIPSAssembler(); + + uint32_t* base() const; + uint32_t* pc() const; + void reset(); + + void disassemble(const char* name); + + void prolog(); + void epilog(uint32_t touched); + int generate(const char* name); + void comment(const char* string); + void label(const char* string); + + // valid only after generate() has been called + uint32_t* pcForLabel(const char* label); + + + // ------------------------------------------------------------------------ + // MIPSAssemblerInterface... + // ------------------------------------------------------------------------ + +#if 0 +#pragma mark - +#pragma mark Arithmetic... +#endif + + void ADDU(int Rd, int Rs, int Rt); + void ADDIU(int Rt, int Rs, int16_t imm); + void SUBU(int Rd, int Rs, int Rt); + void SUBIU(int Rt, int Rs, int16_t imm); + void NEGU(int Rd, int Rs); + void MUL(int Rd, int Rs, int Rt); + void MULT(int Rs, int Rt); // dest is hi,lo + void MULTU(int Rs, int Rt); // dest is hi,lo + void MADD(int Rs, int Rt); // hi,lo = hi,lo + Rs * Rt + void MADDU(int Rs, int Rt); // hi,lo = hi,lo + Rs * Rt + void MSUB(int Rs, int Rt); // hi,lo = hi,lo - Rs * Rt + void MSUBU(int Rs, int Rt); // hi,lo = hi,lo - Rs * Rt + void SEB(int Rd, int Rt); // sign-extend byte (mips32r2) + void SEH(int Rd, int Rt); // sign-extend half-word (mips32r2) + + +#if 0 +#pragma mark - +#pragma mark Comparisons... +#endif + + void SLT(int Rd, int Rs, int Rt); + void SLTI(int Rt, int Rs, int16_t imm); + void SLTU(int Rd, int Rs, int Rt); + void SLTIU(int Rt, int Rs, int16_t imm); + + +#if 0 +#pragma mark - +#pragma mark Logical... +#endif + + void AND(int Rd, int Rs, int Rt); + void ANDI(int Rd, int Rs, uint16_t imm); + void OR(int Rd, int Rs, int Rt); + void ORI(int Rt, int Rs, uint16_t imm); + void NOR(int Rd, int Rs, int Rt); + void NOT(int Rd, int Rs); + void XOR(int Rd, int Rs, int Rt); + void XORI(int Rt, int Rs, uint16_t imm); + + void SLL(int Rd, int Rt, int shft); + void SLLV(int Rd, int Rt, int Rs); + void SRL(int Rd, int Rt, int shft); + void SRLV(int Rd, int Rt, int Rs); + void SRA(int Rd, int Rt, int shft); + void SRAV(int Rd, int Rt, int Rs); + void ROTR(int Rd, int Rt, int shft); // mips32r2 + void ROTRV(int Rd, int Rt, int Rs); // mips32r2 + void RORsyn(int Rd, int Rs, int Rt); // synthetic: d = s rotated by t + void RORIsyn(int Rd, int Rt, int rot); // synthetic: d = s rotated by immed + + void CLO(int Rd, int Rs); + void CLZ(int Rd, int Rs); + void WSBH(int Rd, int Rt); + + +#if 0 +#pragma mark - +#pragma mark Load/store... +#endif + + void LW(int Rt, int Rbase, int16_t offset); + void SW(int Rt, int Rbase, int16_t offset); + void LB(int Rt, int Rbase, int16_t offset); + void LBU(int Rt, int Rbase, int16_t offset); + void SB(int Rt, int Rbase, int16_t offset); + void LH(int Rt, int Rbase, int16_t offset); + void LHU(int Rt, int Rbase, int16_t offset); + void SH(int Rt, int Rbase, int16_t offset); + void LUI(int Rt, int16_t offset); + +#if 0 +#pragma mark - +#pragma mark Register moves... +#endif + + void MOVE(int Rd, int Rs); + void MOVN(int Rd, int Rs, int Rt); + void MOVZ(int Rd, int Rs, int Rt); + void MFHI(int Rd); + void MFLO(int Rd); + void MTHI(int Rs); + void MTLO(int Rs); + +#if 0 +#pragma mark - +#pragma mark Branch... +#endif + + void B(const char* label); + void BEQ(int Rs, int Rt, const char* label); + void BNE(int Rs, int Rt, const char* label); + void BGEZ(int Rs, const char* label); + void BGTZ(int Rs, const char* label); + void BLEZ(int Rs, const char* label); + void BLTZ(int Rs, const char* label); + void JR(int Rs); + + +#if 0 +#pragma mark - +#pragma mark Synthesized Branch... +#endif + + // synthetic variants of above (using slt & friends) + void BEQZ(int Rs, const char* label); + void BNEZ(int Rs, const char* label); + void BGE(int Rs, int Rt, const char* label); + void BGEU(int Rs, int Rt, const char* label); + void BGT(int Rs, int Rt, const char* label); + void BGTU(int Rs, int Rt, const char* label); + void BLE(int Rs, int Rt, const char* label); + void BLEU(int Rs, int Rt, const char* label); + void BLT(int Rs, int Rt, const char* label); + void BLTU(int Rs, int Rt, const char* label); + +#if 0 +#pragma mark - +#pragma mark Misc... +#endif + + void NOP(void); + void NOP2(void); + void UNIMPL(void); + + + + + +private: + void string_detab(char *s); + void string_pad(char *s, int padded_len); + + ArmToMipsAssembler *mParent; + sp<Assembly> mAssembly; + uint32_t* mBase; + uint32_t* mPC; + uint32_t* mPrologPC; + int64_t mDuration; +#if defined(WITH_LIB_HARDWARE) + bool mQemuTracing; +#endif + + struct branch_target_t { + inline branch_target_t() : label(0), pc(0) { } + inline branch_target_t(const char* l, uint32_t* p) + : label(l), pc(p) { } + const char* label; + uint32_t* pc; + }; + + Vector<branch_target_t> mBranchTargets; + KeyedVector< const char*, uint32_t* > mLabels; + KeyedVector< uint32_t*, const char* > mLabelsInverseMapping; + KeyedVector< uint32_t*, const char* > mComments; + + + + + // opcode field of all instructions + enum opcode_field { + spec_op, regimm_op, j_op, jal_op, // 00 + beq_op, bne_op, blez_op, bgtz_op, + addi_op, addiu_op, slti_op, sltiu_op, // 08 + andi_op, ori_op, xori_op, lui_op, + cop0_op, cop1_op, cop2_op, cop1x_op, // 10 + beql_op, bnel_op, blezl_op, bgtzl_op, + daddi_op, daddiu_op, ldl_op, ldr_op, // 18 + spec2_op, jalx_op, mdmx_op, spec3_op, + lb_op, lh_op, lwl_op, lw_op, // 20 + lbu_op, lhu_op, lwr_op, lwu_op, + sb_op, sh_op, swl_op, sw_op, // 28 + sdl_op, sdr_op, swr_op, cache_op, + ll_op, lwc1_op, lwc2_op, pref_op, // 30 + lld_op, ldc1_op, ldc2_op, ld_op, + sc_op, swc1_op, swc2_op, rsrv_3b_op, // 38 + scd_op, sdc1_op, sdc2_op, sd_op + }; + + + // func field for special opcode + enum func_spec_op { + sll_fn, movc_fn, srl_fn, sra_fn, // 00 + sllv_fn, pmon_fn, srlv_fn, srav_fn, + jr_fn, jalr_fn, movz_fn, movn_fn, // 08 + syscall_fn, break_fn, spim_fn, sync_fn, + mfhi_fn, mthi_fn, mflo_fn, mtlo_fn, // 10 + dsllv_fn, rsrv_spec_2, dsrlv_fn, dsrav_fn, + mult_fn, multu_fn, div_fn, divu_fn, // 18 + dmult_fn, dmultu_fn, ddiv_fn, ddivu_fn, + add_fn, addu_fn, sub_fn, subu_fn, // 20 + and_fn, or_fn, xor_fn, nor_fn, + rsrv_spec_3, rsrv_spec_4, slt_fn, sltu_fn, // 28 + dadd_fn, daddu_fn, dsub_fn, dsubu_fn, + tge_fn, tgeu_fn, tlt_fn, tltu_fn, // 30 + teq_fn, rsrv_spec_5, tne_fn, rsrv_spec_6, + dsll_fn, rsrv_spec_7, dsrl_fn, dsra_fn, // 38 + dsll32_fn, rsrv_spec_8, dsrl32_fn, dsra32_fn + }; + + // func field for spec2 opcode + enum func_spec2_op { + madd_fn, maddu_fn, mul_fn, rsrv_spec2_3, + msub_fn, msubu_fn, + clz_fn = 0x20, clo_fn, + dclz_fn = 0x24, dclo_fn, + sdbbp_fn = 0x3f + }; + + // func field for spec3 opcode + enum func_spec3_op { + ext_fn, dextm_fn, dextu_fn, dext_fn, + ins_fn, dinsm_fn, dinsu_fn, dins_fn, + bshfl_fn = 0x20, + dbshfl_fn = 0x24, + rdhwr_fn = 0x3b + }; + + // sa field for spec3 opcodes, with BSHFL function + enum func_spec3_bshfl { + wsbh_fn = 0x02, + seb_fn = 0x10, + seh_fn = 0x18 + }; + + // rt field of regimm opcodes. + enum regimm_fn { + bltz_fn, bgez_fn, bltzl_fn, bgezl_fn, + rsrv_ri_fn4, rsrv_ri_fn5, rsrv_ri_fn6, rsrv_ri_fn7, + tgei_fn, tgeiu_fn, tlti_fn, tltiu_fn, + teqi_fn, rsrv_ri_fn_0d, tnei_fn, rsrv_ri_fn0f, + bltzal_fn, bgezal_fn, bltzall_fn, bgezall_fn, + bposge32_fn= 0x1c, + synci_fn = 0x1f + }; + + + // func field for mad opcodes (MIPS IV). + enum mad_func { + madd_fp_op = 0x08, msub_fp_op = 0x0a, + nmadd_fp_op = 0x0c, nmsub_fp_op = 0x0e + }; + + + enum mips_inst_shifts { + OP_SHF = 26, + JTARGET_SHF = 0, + RS_SHF = 21, + RT_SHF = 16, + RD_SHF = 11, + RE_SHF = 6, + SA_SHF = RE_SHF, // synonym + IMM_SHF = 0, + FUNC_SHF = 0, + + // mask values + MSK_16 = 0xffff, + + + CACHEOP_SHF = 18, + CACHESEL_SHF = 16, + }; +}; + +enum mips_regnames { + R_zero = 0, + R_at, R_v0, R_v1, R_a0, R_a1, R_a2, R_a3, + R_t0, R_t1, R_t2, R_t3, R_t4, R_t5, R_t6, R_t7, + R_s0, R_s1, R_s2, R_s3, R_s4, R_s5, R_s6, R_s7, + R_t8, R_t9, R_k0, R_k1, R_gp, R_sp, R_s8, R_ra, + R_lr = R_s8, + + // arm regs 0-15 are mips regs 2-17 (meaning s0 & s1 are used) + R_at2 = R_s2, // R_at2 = 18 = s2 + R_cmp = R_s3, // R_cmp = 19 = s3 + R_cmp2 = R_s4 // R_cmp2 = 20 = s4 +}; + + + +}; // namespace android + +#endif //ANDROID_MIPSASSEMBLER_H diff --git a/libpixelflinger/codeflinger/load_store.cpp b/libpixelflinger/codeflinger/load_store.cpp index 62aa05c..146fa52 100644 --- a/libpixelflinger/codeflinger/load_store.cpp +++ b/libpixelflinger/codeflinger/load_store.cpp @@ -110,7 +110,11 @@ void GGLAssembler::extract(integer_t& d, int s, int h, int l, int bits) { const int maskLen = h-l; +#ifdef __mips__ + assert(maskLen<=11); +#else assert(maskLen<=8); +#endif assert(h); #if __ARM_ARCH__ >= 7 diff --git a/libpixelflinger/codeflinger/mips_disassem.c b/libpixelflinger/codeflinger/mips_disassem.c new file mode 100644 index 0000000..4ab9bd3 --- /dev/null +++ b/libpixelflinger/codeflinger/mips_disassem.c @@ -0,0 +1,590 @@ +/* $NetBSD: db_disasm.c,v 1.19 2007/02/28 04:21:53 thorpej Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ralph Campbell. + * + * 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 University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)kadb.c 8.1 (Berkeley) 6/10/93 + */ + +#include <stdio.h> +#include <stdint.h> +#include <stdarg.h> +#include <stdbool.h> +#include <sys/cdefs.h> + +#include <sys/types.h> +#include "mips_opcode.h" + + +// #include <sys/systm.h> +// #include <sys/param.h> + +// #include <machine/reg.h> +// #include <machine/cpu.h> +/*#include <machine/param.h>*/ +// #include <machine/db_machdep.h> + +// #include <ddb/db_interface.h> +// #include <ddb/db_output.h> +// #include <ddb/db_extern.h> +// #include <ddb/db_sym.h> + + +static char *sprintf_buffer; +static int sprintf_buf_len; + + +typedef uint32_t db_addr_t; +static void db_printf(const char* fmt, ...); + +static const char * const op_name[64] = { +/* 0 */ "spec", "bcond","j ", "jal", "beq", "bne", "blez", "bgtz", +/* 8 */ "addi", "addiu","slti", "sltiu","andi", "ori", "xori", "lui", +/*16 */ "cop0", "cop1", "cop2", "cop3", "beql", "bnel", "blezl","bgtzl", +/*24 */ "daddi","daddiu","ldl", "ldr", "op34", "op35", "op36", "op37", +/*32 */ "lb ", "lh ", "lwl", "lw ", "lbu", "lhu", "lwr", "lwu", +/*40 */ "sb ", "sh ", "swl", "sw ", "sdl", "sdr", "swr", "cache", +/*48 */ "ll ", "lwc1", "lwc2", "lwc3", "lld", "ldc1", "ldc2", "ld ", +/*56 */ "sc ", "swc1", "swc2", "swc3", "scd", "sdc1", "sdc2", "sd " +}; + +static const char * const spec_name[64] = { +/* 0 */ "sll", "spec01","srl", "sra", "sllv", "spec05","srlv","srav", +/* 8 */ "jr", "jalr", "movz","movn","syscall","break","spec16","sync", +/*16 */ "mfhi", "mthi", "mflo", "mtlo", "dsllv","spec25","dsrlv","dsrav", +/*24 */ "mult", "multu","div", "divu", "dmult","dmultu","ddiv","ddivu", +/*32 */ "add", "addu", "sub", "subu", "and", "or ", "xor", "nor", +/*40 */ "spec50","spec51","slt","sltu", "dadd","daddu","dsub","dsubu", +/*48 */ "tge","tgeu","tlt","tltu","teq","spec65","tne","spec67", +/*56 */ "dsll","spec71","dsrl","dsra","dsll32","spec75","dsrl32","dsra32" +}; + +static const char * const spec2_name[64] = { /* QED RM4650, R5000, etc. */ +/* 0x00 */ "madd", "maddu", "mul", "spec3", "msub", "msubu", "rsrv6", "rsrv7", +/* 0x08 */ "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", +/* 0x10 */ "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", +/* 0x18 */ "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", +/* 0x20 */ "clz", "clo", "rsrv", "rsrv", "dclz", "dclo", "rsrv", "rsrv", +/* 0x28 */ "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", +/* 0x30 */ "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", +/* 0x38 */ "rsrv", "rsrv", "rsrv", "resv", "rsrv", "rsrv", "rsrv", "sdbbp" +}; + +static const char * const bcond_name[32] = { +/* 0 */ "bltz", "bgez", "bltzl", "bgezl", "?", "?", "?", "?", +/* 8 */ "tgei", "tgeiu", "tlti", "tltiu", "teqi", "?", "tnei", "?", +/*16 */ "bltzal", "bgezal", "bltzall", "bgezall", "?", "?", "?", "?", +/*24 */ "?", "?", "?", "?", "?", "?", "?", "?", +}; + +static const char * const cop1_name[64] = { +/* 0 */ "fadd", "fsub", "fmpy", "fdiv", "fsqrt","fabs", "fmov", "fneg", +/* 8 */ "fop08","fop09","fop0a","fop0b","fop0c","fop0d","fop0e","fop0f", +/*16 */ "fop10","fop11","fop12","fop13","fop14","fop15","fop16","fop17", +/*24 */ "fop18","fop19","fop1a","fop1b","fop1c","fop1d","fop1e","fop1f", +/*32 */ "fcvts","fcvtd","fcvte","fop23","fcvtw","fop25","fop26","fop27", +/*40 */ "fop28","fop29","fop2a","fop2b","fop2c","fop2d","fop2e","fop2f", +/*48 */ "fcmp.f","fcmp.un","fcmp.eq","fcmp.ueq","fcmp.olt","fcmp.ult", + "fcmp.ole","fcmp.ule", +/*56 */ "fcmp.sf","fcmp.ngle","fcmp.seq","fcmp.ngl","fcmp.lt","fcmp.nge", + "fcmp.le","fcmp.ngt" +}; + +static const char * const fmt_name[16] = { + "s", "d", "e", "fmt3", + "w", "fmt5", "fmt6", "fmt7", + "fmt8", "fmt9", "fmta", "fmtb", + "fmtc", "fmtd", "fmte", "fmtf" +}; + +#if defined(__mips_n32) || defined(__mips_n64) +static char * const reg_name[32] = { + "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", + "a4", "a5", "a6", "a7", "t0", "t1", "t2", "t3", + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", + "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra" +}; +#else + +static char * alt_arm_reg_name[32] = { // hacked names for comparison with ARM code + "zero", "at", "r0", "r1", "r2", "r3", "r4", "r5", + "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13", + "r14", "r15", "at2", "cmp", "s4", "s5", "s6", "s7", + "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra" +}; + +static char * mips_reg_name[32] = { + "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", + "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", + "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra" +}; + +static char ** reg_name = &mips_reg_name[0]; + +#endif /* __mips_n32 || __mips_n64 */ + +static const char * const c0_opname[64] = { + "c0op00","tlbr", "tlbwi", "c0op03","c0op04","c0op05","tlbwr", "c0op07", + "tlbp", "c0op11","c0op12","c0op13","c0op14","c0op15","c0op16","c0op17", + "rfe", "c0op21","c0op22","c0op23","c0op24","c0op25","c0op26","c0op27", + "eret", "c0op31","c0op32","c0op33","c0op34","c0op35","c0op36","c0op37", + "c0op40","c0op41","c0op42","c0op43","c0op44","c0op45","c0op46","c0op47", + "c0op50","c0op51","c0op52","c0op53","c0op54","c0op55","c0op56","c0op57", + "c0op60","c0op61","c0op62","c0op63","c0op64","c0op65","c0op66","c0op67", + "c0op70","c0op71","c0op72","c0op73","c0op74","c0op75","c0op77","c0op77", +}; + +static const char * const c0_reg[32] = { + "index", "random", "tlblo0", "tlblo1", + "context", "pagemask", "wired", "cp0r7", + "badvaddr", "count", "tlbhi", "compare", + "status", "cause", "epc", "prid", + "config", "lladdr", "watchlo", "watchhi", + "xcontext", "cp0r21", "cp0r22", "debug", + "depc", "perfcnt", "ecc", "cacheerr", + "taglo", "taghi", "errepc", "desave" +}; + +static void print_addr(db_addr_t); +db_addr_t mips_disassem(db_addr_t loc, char *di_buffer, int alt_dis_format); + + +/* + * Disassemble instruction 'insn' nominally at 'loc'. + * 'loc' may in fact contain a breakpoint instruction. + */ +static db_addr_t +db_disasm_insn(int insn, db_addr_t loc, bool altfmt) +{ + bool bdslot = false; + InstFmt i; + + i.word = insn; + + switch (i.JType.op) { + case OP_SPECIAL: + if (i.word == 0) { + db_printf("nop"); + break; + } + if (i.word == 0x0080) { + db_printf("NIY"); + break; + } + if (i.word == 0x00c0) { + db_printf("NOT IMPL"); + break; + } + /* Special cases -------------------------------------------------- + * "addu" is a "move" only in 32-bit mode. What's the correct + * answer - never decode addu/daddu as "move"? + */ + if ( (i.RType.func == OP_ADDU && i.RType.rt == 0) || + (i.RType.func == OP_OR && i.RType.rt == 0) ) { + db_printf("move\t%s,%s", + reg_name[i.RType.rd], + reg_name[i.RType.rs]); + break; + } + // mips32r2, rotr & rotrv + if (i.RType.func == OP_SRL && (i.RType.rs & 1) == 1) { + db_printf("rotr\t%s,%s,%d", reg_name[i.RType.rd], + reg_name[i.RType.rt], i.RType.shamt); + break; + } + if (i.RType.func == OP_SRLV && (i.RType.shamt & 1) == 1) { + db_printf("rotrv\t%s,%s,%s", reg_name[i.RType.rd], + reg_name[i.RType.rt], reg_name[i.RType.rs]); + break; + } + + + db_printf("%s", spec_name[i.RType.func]); + switch (i.RType.func) { + case OP_SLL: + case OP_SRL: + case OP_SRA: + case OP_DSLL: + + case OP_DSRL: + case OP_DSRA: + case OP_DSLL32: + case OP_DSRL32: + case OP_DSRA32: + db_printf("\t%s,%s,%d", + reg_name[i.RType.rd], + reg_name[i.RType.rt], + i.RType.shamt); + break; + + case OP_SLLV: + case OP_SRLV: + case OP_SRAV: + case OP_DSLLV: + case OP_DSRLV: + case OP_DSRAV: + db_printf("\t%s,%s,%s", + reg_name[i.RType.rd], + reg_name[i.RType.rt], + reg_name[i.RType.rs]); + break; + + case OP_MFHI: + case OP_MFLO: + db_printf("\t%s", reg_name[i.RType.rd]); + break; + + case OP_JR: + case OP_JALR: + db_printf("\t%s", reg_name[i.RType.rs]); + bdslot = true; + break; + case OP_MTLO: + case OP_MTHI: + db_printf("\t%s", reg_name[i.RType.rs]); + break; + + case OP_MULT: + case OP_MULTU: + case OP_DMULT: + case OP_DMULTU: + case OP_DIV: + case OP_DIVU: + case OP_DDIV: + case OP_DDIVU: + db_printf("\t%s,%s", + reg_name[i.RType.rs], + reg_name[i.RType.rt]); + break; + + + case OP_SYSCALL: + case OP_SYNC: + break; + + case OP_BREAK: + db_printf("\t%d", (i.RType.rs << 5) | i.RType.rt); + break; + + default: + db_printf("\t%s,%s,%s", + reg_name[i.RType.rd], + reg_name[i.RType.rs], + reg_name[i.RType.rt]); + } + break; + + case OP_SPECIAL2: + if (i.RType.func == OP_MUL) + db_printf("%s\t%s,%s,%s", + spec2_name[i.RType.func & 0x3f], + reg_name[i.RType.rd], + reg_name[i.RType.rs], + reg_name[i.RType.rt]); + else + db_printf("%s\t%s,%s", + spec2_name[i.RType.func & 0x3f], + reg_name[i.RType.rs], + reg_name[i.RType.rt]); + + break; + + case OP_SPECIAL3: + if (i.RType.func == OP_EXT) + db_printf("ext\t%s,%s,%d,%d", + reg_name[i.RType.rt], + reg_name[i.RType.rs], + i.RType.rd+1, + i.RType.shamt); + else if (i.RType.func == OP_INS) + db_printf("ins\t%s,%s,%d,%d", + reg_name[i.RType.rt], + reg_name[i.RType.rs], + i.RType.rd+1, + i.RType.shamt); + else if (i.RType.func == OP_BSHFL && i.RType.shamt == OP_WSBH) + db_printf("wsbh\t%s,%s", + reg_name[i.RType.rd], + reg_name[i.RType.rt]); + else if (i.RType.func == OP_BSHFL && i.RType.shamt == OP_SEB) + db_printf("seb\t%s,%s", + reg_name[i.RType.rd], + reg_name[i.RType.rt]); + else if (i.RType.func == OP_BSHFL && i.RType.shamt == OP_SEH) + db_printf("seh\t%s,%s", + reg_name[i.RType.rd], + reg_name[i.RType.rt]); + else + db_printf("Unknown"); + break; + + case OP_BCOND: + db_printf("%s\t%s,", bcond_name[i.IType.rt], + reg_name[i.IType.rs]); + goto pr_displ; + + case OP_BLEZ: + case OP_BLEZL: + case OP_BGTZ: + case OP_BGTZL: + db_printf("%s\t%s,", op_name[i.IType.op], + reg_name[i.IType.rs]); + goto pr_displ; + + case OP_BEQ: + case OP_BEQL: + if (i.IType.rs == 0 && i.IType.rt == 0) { + db_printf("b \t"); + goto pr_displ; + } + /* FALLTHROUGH */ + case OP_BNE: + case OP_BNEL: + db_printf("%s\t%s,%s,", op_name[i.IType.op], + reg_name[i.IType.rs], + reg_name[i.IType.rt]); + pr_displ: + print_addr(loc + 4 + ((short)i.IType.imm << 2)); + bdslot = true; + break; + + case OP_COP0: + switch (i.RType.rs) { + case OP_BCx: + case OP_BCy: + + db_printf("bc0%c\t", + "ft"[i.RType.rt & COPz_BC_TF_MASK]); + goto pr_displ; + + case OP_MT: + db_printf("mtc0\t%s,%s", + reg_name[i.RType.rt], + c0_reg[i.RType.rd]); + break; + + case OP_DMT: + db_printf("dmtc0\t%s,%s", + reg_name[i.RType.rt], + c0_reg[i.RType.rd]); + break; + + case OP_MF: + db_printf("mfc0\t%s,%s", + reg_name[i.RType.rt], + c0_reg[i.RType.rd]); + break; + + case OP_DMF: + db_printf("dmfc0\t%s,%s", + reg_name[i.RType.rt], + c0_reg[i.RType.rd]); + break; + + default: + db_printf("%s", c0_opname[i.FRType.func]); + } + break; + + case OP_COP1: + switch (i.RType.rs) { + case OP_BCx: + case OP_BCy: + db_printf("bc1%c\t", + "ft"[i.RType.rt & COPz_BC_TF_MASK]); + goto pr_displ; + + case OP_MT: + db_printf("mtc1\t%s,f%d", + reg_name[i.RType.rt], + i.RType.rd); + break; + + case OP_MF: + db_printf("mfc1\t%s,f%d", + reg_name[i.RType.rt], + i.RType.rd); + break; + + case OP_CT: + db_printf("ctc1\t%s,f%d", + reg_name[i.RType.rt], + i.RType.rd); + break; + + case OP_CF: + db_printf("cfc1\t%s,f%d", + reg_name[i.RType.rt], + i.RType.rd); + break; + + default: + db_printf("%s.%s\tf%d,f%d,f%d", + cop1_name[i.FRType.func], + fmt_name[i.FRType.fmt], + i.FRType.fd, i.FRType.fs, i.FRType.ft); + } + break; + + case OP_J: + case OP_JAL: + db_printf("%s\t", op_name[i.JType.op]); + print_addr((loc & 0xF0000000) | (i.JType.target << 2)); + bdslot = true; + break; + + case OP_LWC1: + case OP_SWC1: + db_printf("%s\tf%d,", op_name[i.IType.op], + i.IType.rt); + goto loadstore; + + case OP_LB: + case OP_LH: + case OP_LW: + case OP_LD: + case OP_LBU: + case OP_LHU: + case OP_LWU: + case OP_SB: + case OP_SH: + case OP_SW: + case OP_SD: + db_printf("%s\t%s,", op_name[i.IType.op], + reg_name[i.IType.rt]); + loadstore: + db_printf("%d(%s)", (short)i.IType.imm, + reg_name[i.IType.rs]); + break; + + case OP_ORI: + case OP_XORI: + if (i.IType.rs == 0) { + db_printf("li\t%s,0x%x", + reg_name[i.IType.rt], + i.IType.imm); + break; + } + /* FALLTHROUGH */ + case OP_ANDI: + db_printf("%s\t%s,%s,0x%x", op_name[i.IType.op], + reg_name[i.IType.rt], + reg_name[i.IType.rs], + i.IType.imm); + break; + + case OP_LUI: + db_printf("%s\t%s,0x%x", op_name[i.IType.op], + reg_name[i.IType.rt], + i.IType.imm); + break; + + case OP_CACHE: + db_printf("%s\t0x%x,0x%x(%s)", + op_name[i.IType.op], + i.IType.rt, + i.IType.imm, + reg_name[i.IType.rs]); + break; + + case OP_ADDI: + case OP_DADDI: + case OP_ADDIU: + case OP_DADDIU: + if (i.IType.rs == 0) { + db_printf("li\t%s,%d", + reg_name[i.IType.rt], + (short)i.IType.imm); + break; + } + /* FALLTHROUGH */ + default: + db_printf("%s\t%s,%s,%d", op_name[i.IType.op], + reg_name[i.IType.rt], + reg_name[i.IType.rs], + (short)i.IType.imm); + } + // db_printf("\n"); + // if (bdslot) { + // db_printf(" bd: "); + // mips_disassem(loc+4); + // return (loc + 8); + // } + return (loc + 4); +} + +static void +print_addr(db_addr_t loc) +{ + db_printf("0x%08x", loc); +} + + + +static void db_printf(const char* fmt, ...) +{ + int cnt; + va_list argp; + va_start(argp, fmt); + if (sprintf_buffer) { + cnt = vsnprintf(sprintf_buffer, sprintf_buf_len, fmt, argp); + sprintf_buffer += cnt; + sprintf_buf_len -= cnt; + } else { + vprintf(fmt, argp); + } +} + + +/* + * Disassemble instruction at 'loc'. + * Return address of start of next instruction. + * Since this function is used by 'examine' and by 'step' + * "next instruction" does NOT mean the next instruction to + * be executed but the 'linear' next instruction. + */ +db_addr_t +mips_disassem(db_addr_t loc, char *di_buffer, int alt_dis_format) +{ + u_int32_t instr; + + if (alt_dis_format) { // use ARM register names for disassembly + reg_name = &alt_arm_reg_name[0]; + } + + sprintf_buffer = di_buffer; // quick 'n' dirty printf() vs sprintf() + sprintf_buf_len = 39; // should be passed in + + instr = *(u_int32_t *)loc; + return (db_disasm_insn(instr, loc, false)); +} + diff --git a/libpixelflinger/codeflinger/mips_disassem.h b/libpixelflinger/codeflinger/mips_disassem.h new file mode 100644 index 0000000..2d5b7f5 --- /dev/null +++ b/libpixelflinger/codeflinger/mips_disassem.h @@ -0,0 +1,66 @@ +/* $NetBSD: db_disasm.c,v 1.19 2007/02/28 04:21:53 thorpej Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ralph Campbell. + * + * 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 University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)kadb.c 8.1 (Berkeley) 6/10/93 + */ + + + +#ifndef ANDROID_MIPS_DISASSEM_H +#define ANDROID_MIPS_DISASSEM_H + +#include <sys/types.h> + +#if __cplusplus +extern "C" { +#endif + + +// could add an interface like this, but I have not +// typedef struct { +// u_int (*di_readword)(u_int); +// void (*di_printaddr)(u_int); +// void (*di_printf)(const char *, ...); +// } disasm_interface_t; + +/* Prototypes for callable functions */ + +// u_int disasm(const disasm_interface_t *, u_int, int); + +void mips_disassem(uint32_t *location, char *di_buffer, int alt_fmt); + +#if __cplusplus +} +#endif + +#endif /* !ANDROID_MIPS_DISASSEM_H */ diff --git a/libpixelflinger/codeflinger/mips_opcode.h b/libpixelflinger/codeflinger/mips_opcode.h new file mode 100644 index 0000000..7ed5ef5 --- /dev/null +++ b/libpixelflinger/codeflinger/mips_opcode.h @@ -0,0 +1,316 @@ +/* $NetBSD: mips_opcode.h,v 1.12 2005/12/11 12:18:09 christos Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ralph Campbell. + * + * 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 University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mips_opcode.h 8.1 (Berkeley) 6/10/93 + */ + +/* + * Define the instruction formats and opcode values for the + * MIPS instruction set. + */ + +#include <endian.h> + +/* + * Define the instruction formats. + */ +typedef union { + unsigned word; + +#if BYTE_ORDER == LITTLE_ENDIAN + struct { + unsigned imm: 16; + unsigned rt: 5; + unsigned rs: 5; + unsigned op: 6; + } IType; + + struct { + unsigned target: 26; + unsigned op: 6; + } JType; + + struct { + unsigned func: 6; + unsigned shamt: 5; + unsigned rd: 5; + unsigned rt: 5; + unsigned rs: 5; + unsigned op: 6; + } RType; + + struct { + unsigned func: 6; + unsigned fd: 5; + unsigned fs: 5; + unsigned ft: 5; + unsigned fmt: 4; + unsigned : 1; /* always '1' */ + unsigned op: 6; /* always '0x11' */ + } FRType; +#endif +#if BYTE_ORDER == BIG_ENDIAN + struct { + unsigned op: 6; + unsigned rs: 5; + unsigned rt: 5; + unsigned imm: 16; + } IType; + + struct { + unsigned op: 6; + unsigned target: 26; + } JType; + + struct { + unsigned op: 6; + unsigned rs: 5; + unsigned rt: 5; + unsigned rd: 5; + unsigned shamt: 5; + unsigned func: 6; + } RType; + + struct { + unsigned op: 6; /* always '0x11' */ + unsigned : 1; /* always '1' */ + unsigned fmt: 4; + unsigned ft: 5; + unsigned fs: 5; + unsigned fd: 5; + unsigned func: 6; + } FRType; +#endif +} InstFmt; + +/* + * Values for the 'op' field. + */ +#define OP_SPECIAL 000 +#define OP_BCOND 001 +#define OP_J 002 +#define OP_JAL 003 +#define OP_BEQ 004 +#define OP_BNE 005 +#define OP_BLEZ 006 +#define OP_BGTZ 007 + +#define OP_ADDI 010 +#define OP_ADDIU 011 +#define OP_SLTI 012 +#define OP_SLTIU 013 +#define OP_ANDI 014 +#define OP_ORI 015 +#define OP_XORI 016 +#define OP_LUI 017 + +#define OP_COP0 020 +#define OP_COP1 021 +#define OP_COP2 022 +#define OP_COP3 023 +#define OP_BEQL 024 /* MIPS-II, for r4000 port */ +#define OP_BNEL 025 /* MIPS-II, for r4000 port */ +#define OP_BLEZL 026 /* MIPS-II, for r4000 port */ +#define OP_BGTZL 027 /* MIPS-II, for r4000 port */ + +#define OP_DADDI 030 /* MIPS-II, for r4000 port */ +#define OP_DADDIU 031 /* MIPS-II, for r4000 port */ +#define OP_LDL 032 /* MIPS-II, for r4000 port */ +#define OP_LDR 033 /* MIPS-II, for r4000 port */ + +#define OP_SPECIAL2 034 /* QED opcodes */ +#define OP_SPECIAL3 037 /* mips32r2 opcodes */ + +#define OP_LB 040 +#define OP_LH 041 +#define OP_LWL 042 +#define OP_LW 043 +#define OP_LBU 044 +#define OP_LHU 045 +#define OP_LWR 046 +#define OP_LHU 045 +#define OP_LWR 046 +#define OP_LWU 047 /* MIPS-II, for r4000 port */ + +#define OP_SB 050 +#define OP_SH 051 +#define OP_SWL 052 +#define OP_SW 053 +#define OP_SDL 054 /* MIPS-II, for r4000 port */ +#define OP_SDR 055 /* MIPS-II, for r4000 port */ +#define OP_SWR 056 +#define OP_CACHE 057 /* MIPS-II, for r4000 port */ + +#define OP_LL 060 +#define OP_LWC0 OP_LL /* backwards source compatibility */ +#define OP_LWC1 061 +#define OP_LWC2 062 +#define OP_LWC3 063 +#define OP_LLD 064 /* MIPS-II, for r4000 port */ +#define OP_LDC1 065 +#define OP_LD 067 /* MIPS-II, for r4000 port */ + +#define OP_SC 070 +#define OP_SWC0 OP_SC /* backwards source compatibility */ +#define OP_SWC1 071 +#define OP_SWC2 072 +#define OP_SWC3 073 +#define OP_SCD 074 /* MIPS-II, for r4000 port */ +#define OP_SDC1 075 +#define OP_SD 077 /* MIPS-II, for r4000 port */ + +/* + * Values for the 'func' field when 'op' == OP_SPECIAL. + */ +#define OP_SLL 000 +#define OP_SRL 002 +#define OP_SRA 003 +#define OP_SLLV 004 +#define OP_SRLV 006 +#define OP_SRAV 007 + +#define OP_JR 010 +#define OP_JALR 011 +#define OP_SYSCALL 014 +#define OP_BREAK 015 +#define OP_SYNC 017 /* MIPS-II, for r4000 port */ + +#define OP_MFHI 020 +#define OP_MTHI 021 +#define OP_MFLO 022 +#define OP_MTLO 023 +#define OP_DSLLV 024 /* MIPS-II, for r4000 port */ +#define OP_DSRLV 026 /* MIPS-II, for r4000 port */ +#define OP_DSRAV 027 /* MIPS-II, for r4000 port */ + +#define OP_MULT 030 +#define OP_MULTU 031 +#define OP_DIV 032 +#define OP_DIVU 033 +#define OP_DMULT 034 /* MIPS-II, for r4000 port */ +#define OP_DMULTU 035 /* MIPS-II, for r4000 port */ +#define OP_DDIV 036 /* MIPS-II, for r4000 port */ +#define OP_DDIVU 037 /* MIPS-II, for r4000 port */ + +#define OP_ADD 040 +#define OP_ADDU 041 +#define OP_SUB 042 +#define OP_SUBU 043 +#define OP_AND 044 +#define OP_OR 045 +#define OP_XOR 046 +#define OP_NOR 047 + +#define OP_SLT 052 +#define OP_SLTU 053 +#define OP_DADD 054 /* MIPS-II, for r4000 port */ +#define OP_DADDU 055 /* MIPS-II, for r4000 port */ +#define OP_DSUB 056 /* MIPS-II, for r4000 port */ +#define OP_DSUBU 057 /* MIPS-II, for r4000 port */ + +#define OP_TGE 060 /* MIPS-II, for r4000 port */ +#define OP_TGEU 061 /* MIPS-II, for r4000 port */ +#define OP_TLT 062 /* MIPS-II, for r4000 port */ +#define OP_TLTU 063 /* MIPS-II, for r4000 port */ +#define OP_TEQ 064 /* MIPS-II, for r4000 port */ +#define OP_TNE 066 /* MIPS-II, for r4000 port */ + +#define OP_DSLL 070 /* MIPS-II, for r4000 port */ +#define OP_DSRL 072 /* MIPS-II, for r4000 port */ +#define OP_DSRA 073 /* MIPS-II, for r4000 port */ +#define OP_DSLL32 074 /* MIPS-II, for r4000 port */ +#define OP_DSRL32 076 /* MIPS-II, for r4000 port */ +#define OP_DSRA32 077 /* MIPS-II, for r4000 port */ + +/* + * Values for the 'func' field when 'op' == OP_SPECIAL2. + */ +#define OP_MAD 000 /* QED */ +#define OP_MADU 001 /* QED */ +#define OP_MUL 002 /* QED */ + +/* + * Values for the 'func' field when 'op' == OP_SPECIAL3. + */ +#define OP_EXT 000 +#define OP_INS 004 +#define OP_BSHFL 040 + +/* + * Values for the 'shamt' field when OP_SPECIAL3 && func OP_BSHFL. + */ +#define OP_WSBH 002 +#define OP_SEB 020 +#define OP_SEH 030 + +/* + * Values for the 'func' field when 'op' == OP_BCOND. + */ +#define OP_BLTZ 000 +#define OP_BGEZ 001 +#define OP_BLTZL 002 /* MIPS-II, for r4000 port */ +#define OP_BGEZL 003 /* MIPS-II, for r4000 port */ + +#define OP_TGEI 010 /* MIPS-II, for r4000 port */ +#define OP_TGEIU 011 /* MIPS-II, for r4000 port */ +#define OP_TLTI 012 /* MIPS-II, for r4000 port */ +#define OP_TLTIU 013 /* MIPS-II, for r4000 port */ +#define OP_TEQI 014 /* MIPS-II, for r4000 port */ +#define OP_TNEI 016 /* MIPS-II, for r4000 port */ + +#define OP_BLTZAL 020 /* MIPS-II, for r4000 port */ +#define OP_BGEZAL 021 +#define OP_BLTZALL 022 +#define OP_BGEZALL 023 + +/* + * Values for the 'rs' field when 'op' == OP_COPz. + */ +#define OP_MF 000 +#define OP_DMF 001 /* MIPS-II, for r4000 port */ +#define OP_MT 004 +#define OP_DMT 005 /* MIPS-II, for r4000 port */ +#define OP_BCx 010 +#define OP_BCy 014 +#define OP_CF 002 +#define OP_CT 006 + +/* + * Values for the 'rt' field when 'op' == OP_COPz. + */ +#define COPz_BC_TF_MASK 0x01 +#define COPz_BC_TRUE 0x01 +#define COPz_BC_FALSE 0x00 +#define COPz_BCL_TF_MASK 0x02 /* MIPS-II, for r4000 port */ +#define COPz_BCL_TRUE 0x02 /* MIPS-II, for r4000 port */ +#define COPz_BCL_FALSE 0x00 /* MIPS-II, for r4000 port */ diff --git a/libpixelflinger/codeflinger/texturing.cpp b/libpixelflinger/codeflinger/texturing.cpp index 8464fbd..4d5a50f 100644 --- a/libpixelflinger/codeflinger/texturing.cpp +++ b/libpixelflinger/codeflinger/texturing.cpp @@ -464,6 +464,9 @@ void GGLAssembler::build_textures( fragment_parts_t& parts, CONTEXT_LOAD(t.reg, generated_vars.texture[i].spill[1]); } + if (registerFile().status() & RegisterFile::OUT_OF_REGISTERS) + return; + comment("compute repeat/clamp"); int u = scratches.obtain(); int v = scratches.obtain(); @@ -472,6 +475,9 @@ void GGLAssembler::build_textures( fragment_parts_t& parts, int U = 0; int V = 0; + if (registerFile().status() & RegisterFile::OUT_OF_REGISTERS) + return; + CONTEXT_LOAD(width, generated_vars.texture[i].width); CONTEXT_LOAD(height, generated_vars.texture[i].height); @@ -510,6 +516,9 @@ void GGLAssembler::build_textures( fragment_parts_t& parts, U = scratches.obtain(); V = scratches.obtain(); + if (registerFile().status() & RegisterFile::OUT_OF_REGISTERS) + return; + // sample the texel center SUB(AL, 0, u, u, imm(1<<(FRAC_BITS-1))); SUB(AL, 0, v, v, imm(1<<(FRAC_BITS-1))); @@ -593,6 +602,10 @@ void GGLAssembler::build_textures( fragment_parts_t& parts, comment("iterate s,t"); int dsdx = scratches.obtain(); int dtdx = scratches.obtain(); + + if (registerFile().status() & RegisterFile::OUT_OF_REGISTERS) + return; + CONTEXT_LOAD(dsdx, generated_vars.texture[i].dsdx); CONTEXT_LOAD(dtdx, generated_vars.texture[i].dtdx); ADD(AL, 0, s.reg, s.reg, dsdx); @@ -611,6 +624,10 @@ void GGLAssembler::build_textures( fragment_parts_t& parts, texel.setTo(regs.obtain(), &tmu.format); txPtr.setTo(texel.reg, tmu.bits); int stride = scratches.obtain(); + + if (registerFile().status() & RegisterFile::OUT_OF_REGISTERS) + return; + CONTEXT_LOAD(stride, generated_vars.texture[i].stride); CONTEXT_LOAD(txPtr.reg, generated_vars.texture[i].data); SMLABB(AL, u, v, stride, u); // u+v*stride @@ -1078,6 +1095,7 @@ void GGLAssembler::build_texture_environment( Scratch scratches(registerFile()); pixel_t texel(parts.texel[i]); + if (multiTexture && tmu.swrap == GGL_NEEDS_WRAP_11 && tmu.twrap == GGL_NEEDS_WRAP_11) diff --git a/libpixelflinger/scanline.cpp b/libpixelflinger/scanline.cpp index d1f3d96..a5d28b2 100644 --- a/libpixelflinger/scanline.cpp +++ b/libpixelflinger/scanline.cpp @@ -32,6 +32,9 @@ #include "codeflinger/CodeCache.h" #include "codeflinger/GGLAssembler.h" #include "codeflinger/ARMAssembler.h" +#if defined(__mips__) +#include "codeflinger/MIPSAssembler.h" +#endif //#include "codeflinger/ARMAssemblerOptimizer.h" // ---------------------------------------------------------------------------- @@ -49,7 +52,7 @@ # define ANDROID_CODEGEN ANDROID_CODEGEN_GENERATED #endif -#if defined(__arm__) +#if defined(__arm__) || defined(__mips__) # define ANDROID_ARM_CODEGEN 1 #else # define ANDROID_ARM_CODEGEN 0 @@ -63,7 +66,11 @@ */ #define DEBUG_NEEDS 0 +#ifdef __mips__ +#define ASSEMBLY_SCRATCH_SIZE 4096 +#else #define ASSEMBLY_SCRATCH_SIZE 2048 +#endif // ---------------------------------------------------------------------------- namespace android { @@ -266,7 +273,12 @@ static const needs_filter_t fill16noblend = { // ---------------------------------------------------------------------------- #if ANDROID_ARM_CODEGEN + +#if defined(__mips__) +static CodeCache gCodeCache(32 * 1024); +#else static CodeCache gCodeCache(12 * 1024); +#endif class ScanlineAssembly : public Assembly { AssemblyKey<needs_t> mKey; @@ -375,9 +387,14 @@ static void pick_scanline(context_t* c) sp<ScanlineAssembly> a = new ScanlineAssembly(c->state.needs, ASSEMBLY_SCRATCH_SIZE); // initialize our assembler +#if defined(__arm__) GGLAssembler assembler( new ARMAssembler(a) ); //GGLAssembler assembler( // new ARMAssemblerOptimizer(new ARMAssembler(a)) ); +#endif +#if defined(__mips__) + GGLAssembler assembler( new ArmToMipsAssembler(a) ); +#endif // generate the scanline code for the given needs int err = assembler.scanline(c->state.needs, c); if (ggl_likely(!err)) { diff --git a/libpixelflinger/tests/codegen/codegen.cpp b/libpixelflinger/tests/codegen/codegen.cpp index 94e2481..3d5a040 100644 --- a/libpixelflinger/tests/codegen/codegen.cpp +++ b/libpixelflinger/tests/codegen/codegen.cpp @@ -9,14 +9,19 @@ #include "codeflinger/CodeCache.h" #include "codeflinger/GGLAssembler.h" #include "codeflinger/ARMAssembler.h" +#include "codeflinger/MIPSAssembler.h" -#if defined(__arm__) +#if defined(__arm__) || defined(__mips__) # define ANDROID_ARM_CODEGEN 1 #else # define ANDROID_ARM_CODEGEN 0 #endif +#if defined (__mips__) +#define ASSEMBLY_SCRATCH_SIZE 4096 +#else #define ASSEMBLY_SCRATCH_SIZE 2048 +#endif using namespace android; @@ -39,14 +44,22 @@ static void ggl_test_codegen(uint32_t n, uint32_t p, uint32_t t0, uint32_t t1) needs.t[0] = t0; needs.t[1] = t1; sp<ScanlineAssembly> a(new ScanlineAssembly(needs, ASSEMBLY_SCRATCH_SIZE)); + +#if defined(__arm__) GGLAssembler assembler( new ARMAssembler(a) ); +#endif + +#if defined(__mips__) + GGLAssembler assembler( new ArmToMipsAssembler(a) ); +#endif + int err = assembler.scanline(needs, (context_t*)c); if (err != 0) { printf("error %08x (%s)\n", err, strerror(-err)); } gglUninit(c); #else - printf("This test runs only on ARM\n"); + printf("This test runs only on ARM or MIPS\n"); #endif } 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 @@ libsuspend_libraries := \ liblog libcutils include $(CLEAR_VARS) - LOCAL_SRC_FILES := $(libsuspend_src_files) LOCAL_MODULE := libsuspend LOCAL_MODULE_TAGS := optional @@ -21,3 +20,12 @@ LOCAL_C_INCLUDES += $(LOCAL_PATH)/include 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/libsuspend/autosuspend.c b/libsuspend/autosuspend.c index 7d1d973..eb1f66e 100644 --- a/libsuspend/autosuspend.c +++ b/libsuspend/autosuspend.c @@ -33,8 +33,6 @@ static int autosuspend_init(void) return 0; } - autosuspend_inited = true; - autosuspend_ops = autosuspend_earlysuspend_init(); if (autosuspend_ops) { goto out; @@ -56,6 +54,8 @@ static int autosuspend_init(void) } out: + autosuspend_inited = true; + ALOGV("autosuspend initialized\n"); return 0; } diff --git a/libsuspend/autosuspend_autosleep.c b/libsuspend/autosuspend_autosleep.c index dd0dfef..5451615 100644 --- a/libsuspend/autosuspend_autosleep.c +++ b/libsuspend/autosuspend_autosleep.c @@ -95,5 +95,8 @@ struct autosuspend_ops *autosuspend_autosleep_init(void) } ALOGI("Selected autosleep\n"); + + autosuspend_autosleep_disable(); + return &autosuspend_autosleep_ops; } diff --git a/libsuspend/autosuspend_earlysuspend.c b/libsuspend/autosuspend_earlysuspend.c index 2c2aa36..1df8c6a 100644 --- a/libsuspend/autosuspend_earlysuspend.c +++ b/libsuspend/autosuspend_earlysuspend.c @@ -16,6 +16,8 @@ #include <errno.h> #include <fcntl.h> +#include <pthread.h> +#include <stdbool.h> #include <stddef.h> #include <string.h> #include <sys/types.h> @@ -31,11 +33,75 @@ #define EARLYSUSPEND_WAIT_FOR_FB_SLEEP "/sys/power/wait_for_fb_sleep" #define EARLYSUSPEND_WAIT_FOR_FB_WAKE "/sys/power/wait_for_fb_wake" - static int sPowerStatefd; static const char *pwr_state_mem = "mem"; static const char *pwr_state_on = "on"; +static pthread_t earlysuspend_thread; +static pthread_mutex_t earlysuspend_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t earlysuspend_cond = PTHREAD_COND_INITIALIZER; +static bool wait_for_earlysuspend; +static enum { + EARLYSUSPEND_ON, + EARLYSUSPEND_MEM, +} earlysuspend_state = EARLYSUSPEND_ON; + +int wait_for_fb_wake(void) +{ + int err = 0; + char buf; + int fd = open(EARLYSUSPEND_WAIT_FOR_FB_WAKE, O_RDONLY, 0); + // if the file doesn't exist, the error will be caught in read() below + do { + err = read(fd, &buf, 1); + } while (err < 0 && errno == EINTR); + ALOGE_IF(err < 0, + "*** ANDROID_WAIT_FOR_FB_WAKE failed (%s)", strerror(errno)); + close(fd); + return err < 0 ? err : 0; +} + +static int wait_for_fb_sleep(void) +{ + int err = 0; + char buf; + int fd = open(EARLYSUSPEND_WAIT_FOR_FB_SLEEP, O_RDONLY, 0); + // if the file doesn't exist, the error will be caught in read() below + do { + err = read(fd, &buf, 1); + } while (err < 0 && errno == EINTR); + ALOGE_IF(err < 0, + "*** ANDROID_WAIT_FOR_FB_SLEEP failed (%s)", strerror(errno)); + close(fd); + return err < 0 ? err : 0; +} + +static void *earlysuspend_thread_func(void *arg) +{ + char buf[80]; + char wakeup_count[20]; + int wakeup_count_len; + int ret; + while (1) { + if (wait_for_fb_sleep()) { + ALOGE("Failed reading wait_for_fb_sleep, exiting earlysuspend thread\n"); + return NULL; + } + pthread_mutex_lock(&earlysuspend_mutex); + earlysuspend_state = EARLYSUSPEND_MEM; + pthread_cond_signal(&earlysuspend_cond); + pthread_mutex_unlock(&earlysuspend_mutex); + + if (wait_for_fb_wake()) { + ALOGE("Failed reading wait_for_fb_wake, exiting earlysuspend thread\n"); + return NULL; + } + pthread_mutex_lock(&earlysuspend_mutex); + earlysuspend_state = EARLYSUSPEND_ON; + pthread_cond_signal(&earlysuspend_cond); + pthread_mutex_unlock(&earlysuspend_mutex); + } +} static int autosuspend_earlysuspend_enable(void) { char buf[80]; @@ -50,6 +116,14 @@ static int autosuspend_earlysuspend_enable(void) goto err; } + if (wait_for_earlysuspend) { + pthread_mutex_lock(&earlysuspend_mutex); + while (earlysuspend_state != EARLYSUSPEND_MEM) { + pthread_cond_wait(&earlysuspend_cond, &earlysuspend_mutex); + } + pthread_mutex_unlock(&earlysuspend_mutex); + } + ALOGV("autosuspend_earlysuspend_enable done\n"); return 0; @@ -72,6 +146,14 @@ static int autosuspend_earlysuspend_disable(void) goto err; } + if (wait_for_earlysuspend) { + pthread_mutex_lock(&earlysuspend_mutex); + while (earlysuspend_state != EARLYSUSPEND_ON) { + pthread_cond_wait(&earlysuspend_cond, &earlysuspend_mutex); + } + pthread_mutex_unlock(&earlysuspend_mutex); + } + ALOGV("autosuspend_earlysuspend_disable done\n"); return 0; @@ -85,29 +167,61 @@ struct autosuspend_ops autosuspend_earlysuspend_ops = { .disable = autosuspend_earlysuspend_disable, }; -struct autosuspend_ops *autosuspend_earlysuspend_init(void) +void start_earlysuspend_thread(void) { char buf[80]; int ret; ret = access(EARLYSUSPEND_WAIT_FOR_FB_SLEEP, F_OK); if (ret < 0) { - return NULL; + return; } ret = access(EARLYSUSPEND_WAIT_FOR_FB_WAKE, F_OK); if (ret < 0) { - return NULL; + return; } + wait_for_fb_wake(); + + ALOGI("Starting early suspend unblocker thread\n"); + ret = pthread_create(&earlysuspend_thread, NULL, earlysuspend_thread_func, NULL); + if (ret) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error creating thread: %s\n", buf); + return; + } + + wait_for_earlysuspend = true; +} + +struct autosuspend_ops *autosuspend_earlysuspend_init(void) +{ + char buf[80]; + int ret; + sPowerStatefd = open(EARLYSUSPEND_SYS_POWER_STATE, O_RDWR); if (sPowerStatefd < 0) { strerror_r(errno, buf, sizeof(buf)); - ALOGE("Error opening %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf); + ALOGW("Error opening %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf); return NULL; } + ret = write(sPowerStatefd, "on", 2); + if (ret < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGW("Error writing 'on' to %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf); + goto err_write; + } + ALOGI("Selected early suspend\n"); + + start_earlysuspend_thread(); + return &autosuspend_earlysuspend_ops; + +err_write: + close(sPowerStatefd); + return NULL; } diff --git a/libsync/sync.c b/libsync/sync.c index 311da14..4892866 100644 --- a/libsync/sync.c +++ b/libsync/sync.c @@ -20,15 +20,16 @@ #include <stdint.h> #include <string.h> +#include <linux/sync.h> +#include <linux/sw_sync.h> + #include <sys/ioctl.h> #include <sys/stat.h> #include <sys/types.h> -#include <sync/sync.h> - -int sync_wait(int fd, unsigned int timeout) +int sync_wait(int fd, int timeout) { - __u32 to = timeout; + __s32 to = timeout; return ioctl(fd, SYNC_IOC_WAIT, &to); } diff --git a/libsysutils/src/FrameworkListener.cpp b/libsysutils/src/FrameworkListener.cpp index 6731cf1..02a401d 100644 --- a/libsysutils/src/FrameworkListener.cpp +++ b/libsysutils/src/FrameworkListener.cpp @@ -25,6 +25,8 @@ #include <sysutils/FrameworkCommand.h> #include <sysutils/SocketClient.h> +static const int CMD_BUF_SIZE = 1024; + FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) : SocketListener(socketName, true, withSeq) { init(socketName, withSeq); @@ -43,7 +45,7 @@ void FrameworkListener::init(const char *socketName, bool withSeq) { } bool FrameworkListener::onDataAvailable(SocketClient *c) { - char buffer[255]; + char buffer[CMD_BUF_SIZE]; int len; len = TEMP_FAILURE_RETRY(read(c->getSocket(), buffer, sizeof(buffer))); @@ -52,6 +54,8 @@ bool FrameworkListener::onDataAvailable(SocketClient *c) { return false; } else if (!len) return false; + if(buffer[len-1] != '\0') + SLOGW("String is not zero-terminated"); int offset = 0; int i; @@ -63,6 +67,7 @@ bool FrameworkListener::onDataAvailable(SocketClient *c) { offset = i + 1; } } + return true; } @@ -74,7 +79,7 @@ void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) { FrameworkCommandCollection::iterator i; int argc = 0; char *argv[FrameworkListener::CMD_ARGS_MAX]; - char tmp[255]; + char tmp[CMD_BUF_SIZE]; char *p = data; char *q = tmp; char *qlimit = tmp + sizeof(tmp) - 1; @@ -180,7 +185,6 @@ void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) { goto out; } } - cli->sendMsg(500, "Command not recognized", false); out: int j; diff --git a/libusbhost/Android.mk b/libusbhost/Android.mk index 52b4ead..9565cc5 100644 --- a/libusbhost/Android.mk +++ b/libusbhost/Android.mk @@ -44,3 +44,13 @@ LOCAL_CFLAGS := -g -DUSE_LIBLOG LOCAL_SHARED_LIBRARIES := libcutils include $(BUILD_SHARED_LIBRARY) + +# Static library for target +# ======================================================== + +include $(CLEAR_VARS) + +LOCAL_MODULE := libusbhost +LOCAL_SRC_FILES := usbhost.c + +include $(BUILD_STATIC_LIBRARY) diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c index 5d261cd..167fa60 100644 --- a/libusbhost/usbhost.c +++ b/libusbhost/usbhost.c @@ -33,6 +33,7 @@ #include <stdlib.h> #include <unistd.h> #include <string.h> +#include <stddef.h> #include <sys/ioctl.h> #include <sys/types.h> @@ -49,16 +50,24 @@ #include "usbhost/usbhost.h" -#define USB_FS_DIR "/dev/bus/usb" -#define USB_FS_ID_SCANNER "/dev/bus/usb/%d/%d" -#define USB_FS_ID_FORMAT "/dev/bus/usb/%03d/%03d" +#define DEV_DIR "/dev" +#define USB_FS_DIR DEV_DIR "/bus/usb" +#define USB_FS_ID_SCANNER USB_FS_DIR "/%d/%d" +#define USB_FS_ID_FORMAT USB_FS_DIR "/%03d/%03d" // From drivers/usb/core/devio.c // I don't know why this isn't in a kernel header #define MAX_USBFS_BUFFER_SIZE 16384 +#define MAX_USBFS_WD_COUNT 10 + struct usb_host_context { - int fd; + int fd; + usb_device_added_cb cb_added; + usb_device_removed_cb cb_removed; + void *data; + int wds[MAX_USBFS_WD_COUNT]; + int wdd; }; struct usb_device { @@ -77,39 +86,72 @@ static inline int badname(const char *name) return 0; } +static int find_existing_devices_bus(char *busname, + usb_device_added_cb added_cb, + void *client_data) +{ + char devname[32]; + DIR *devdir; + struct dirent *de; + int done = 0; + + devdir = opendir(busname); + if(devdir == 0) return 0; + + while ((de = readdir(devdir)) && !done) { + if(badname(de->d_name)) continue; + + snprintf(devname, sizeof(devname), "%s/%s", busname, de->d_name); + done = added_cb(devname, client_data); + } // end of devdir while + closedir(devdir); + + return done; +} + /* returns true if one of the callbacks indicates we are done */ static int find_existing_devices(usb_device_added_cb added_cb, - usb_device_removed_cb removed_cb, void *client_data) { - char busname[32], devname[32]; - DIR *busdir , *devdir ; + char busname[32]; + DIR *busdir; struct dirent *de; int done = 0; busdir = opendir(USB_FS_DIR); - if(busdir == 0) return 1; + if(busdir == 0) return 0; while ((de = readdir(busdir)) != 0 && !done) { if(badname(de->d_name)) continue; - snprintf(busname, sizeof busname, "%s/%s", USB_FS_DIR, de->d_name); - devdir = opendir(busname); - if(devdir == 0) continue; - - while ((de = readdir(devdir)) && !done) { - if(badname(de->d_name)) continue; - - snprintf(devname, sizeof devname, "%s/%s", busname, de->d_name); - done = added_cb(devname, client_data); - } // end of devdir while - closedir(devdir); + snprintf(busname, sizeof(busname), USB_FS_DIR "/%s", de->d_name); + done = find_existing_devices_bus(busname, added_cb, + client_data); } //end of busdir while closedir(busdir); return done; } +static void watch_existing_subdirs(struct usb_host_context *context, + int *wds, int wd_count) +{ + char path[100]; + int i, ret; + + wds[0] = inotify_add_watch(context->fd, USB_FS_DIR, IN_CREATE | IN_DELETE); + if (wds[0] < 0) + return; + + /* watch existing subdirectories of USB_FS_DIR */ + for (i = 1; i < wd_count; i++) { + snprintf(path, sizeof(path), USB_FS_DIR "/%03d", i); + ret = inotify_add_watch(context->fd, path, IN_CREATE | IN_DELETE); + if (ret >= 0) + wds[i] = ret; + } +} + struct usb_host_context *usb_host_init() { struct usb_host_context *context = calloc(1, sizeof(struct usb_host_context)); @@ -132,76 +174,126 @@ void usb_host_cleanup(struct usb_host_context *context) free(context); } -void usb_host_run(struct usb_host_context *context, +int usb_host_get_fd(struct usb_host_context *context) +{ + return context->fd; +} /* usb_host_get_fd() */ + +int usb_host_load(struct usb_host_context *context, usb_device_added_cb added_cb, usb_device_removed_cb removed_cb, usb_discovery_done_cb discovery_done_cb, void *client_data) { - struct inotify_event* event; - char event_buf[512]; - char path[100]; - int i, ret, done = 0; - int wd, wds[10]; - int wd_count = sizeof(wds) / sizeof(wds[0]); + int done = 0; + int i; + + context->cb_added = added_cb; + context->cb_removed = removed_cb; + context->data = client_data; D("Created device discovery thread\n"); /* watch for files added and deleted within USB_FS_DIR */ - memset(wds, 0, sizeof(wds)); + for (i = 0; i < MAX_USBFS_WD_COUNT; i++) + context->wds[i] = -1; + /* watch the root for new subdirectories */ - wds[0] = inotify_add_watch(context->fd, USB_FS_DIR, IN_CREATE | IN_DELETE); - if (wds[0] < 0) { + context->wdd = inotify_add_watch(context->fd, DEV_DIR, IN_CREATE | IN_DELETE); + if (context->wdd < 0) { fprintf(stderr, "inotify_add_watch failed\n"); if (discovery_done_cb) discovery_done_cb(client_data); - return; + return done; } - /* watch existing subdirectories of USB_FS_DIR */ - for (i = 1; i < wd_count; i++) { - snprintf(path, sizeof(path), "%s/%03d", USB_FS_DIR, i); - ret = inotify_add_watch(context->fd, path, IN_CREATE | IN_DELETE); - if (ret > 0) - wds[i] = ret; - } + watch_existing_subdirs(context, context->wds, MAX_USBFS_WD_COUNT); /* check for existing devices first, after we have inotify set up */ - done = find_existing_devices(added_cb, removed_cb, client_data); + done = find_existing_devices(added_cb, client_data); if (discovery_done_cb) done |= discovery_done_cb(client_data); - while (!done) { - ret = read(context->fd, event_buf, sizeof(event_buf)); - if (ret >= (int)sizeof(struct inotify_event)) { - event = (struct inotify_event *)event_buf; - wd = event->wd; - if (wd == wds[0]) { - i = atoi(event->name); - snprintf(path, sizeof(path), "%s/%s", USB_FS_DIR, event->name); - D("new subdirectory %s: index: %d\n", path, i); - if (i > 0 && i < wd_count) { - ret = inotify_add_watch(context->fd, path, IN_CREATE | IN_DELETE); - if (ret > 0) - wds[i] = ret; + return done; +} /* usb_host_load() */ + +int usb_host_read_event(struct usb_host_context *context) +{ + struct inotify_event* event; + char event_buf[512]; + char path[100]; + int i, ret, done = 0; + int j, event_size; + int wd; + + ret = read(context->fd, event_buf, sizeof(event_buf)); + if (ret >= (int)sizeof(struct inotify_event)) { + event = (struct inotify_event *)event_buf; + wd = event->wd; + if (wd == context->wdd) { + if ((event->mask & IN_CREATE) && !strcmp(event->name, "bus")) { + watch_existing_subdirs(context, context->wds, MAX_USBFS_WD_COUNT); + done = find_existing_devices(context->cb_added, context->data); + } else if ((event->mask & IN_DELETE) && !strcmp(event->name, "bus")) { + for (i = 0; i < MAX_USBFS_WD_COUNT; i++) { + if (context->wds[i] >= 0) { + inotify_rm_watch(context->fd, context->wds[i]); + context->wds[i] = -1; + } + } + } + } else if (wd == context->wds[0]) { + i = atoi(event->name); + snprintf(path, sizeof(path), USB_FS_DIR "/%s", event->name); + D("%s subdirectory %s: index: %d\n", (event->mask & IN_CREATE) ? + "new" : "gone", path, i); + if (i > 0 && i < MAX_USBFS_WD_COUNT) { + if (event->mask & IN_CREATE) { + ret = inotify_add_watch(context->fd, path, + IN_CREATE | IN_DELETE); + if (ret >= 0) + context->wds[i] = ret; + done = find_existing_devices_bus(path, context->cb_added, + context->data); + } else if (event->mask & IN_DELETE) { + inotify_rm_watch(context->fd, context->wds[i]); + context->wds[i] = -1; } - } else { - for (i = 1; i < wd_count && !done; i++) { - if (wd == wds[i]) { - snprintf(path, sizeof(path), "%s/%03d/%s", USB_FS_DIR, i, event->name); - if (event->mask == IN_CREATE) { - D("new device %s\n", path); - done = added_cb(path, client_data); - } else if (event->mask == IN_DELETE) { - D("gone device %s\n", path); - done = removed_cb(path, client_data); - } + } + } else { + for (i = 1; (i < MAX_USBFS_WD_COUNT) && !done; i++) { + if (wd == context->wds[i]) { + snprintf(path, sizeof(path), USB_FS_DIR "/%03d/%s", i, event->name); + if (event->mask == IN_CREATE) { + D("new device %s\n", path); + done = context->cb_added(path, context->data); + } else if (event->mask == IN_DELETE) { + D("gone device %s\n", path); + done = context->cb_removed(path, context->data); } } } } } -} + + return done; +} /* usb_host_read_event() */ + +void usb_host_run(struct usb_host_context *context, + usb_device_added_cb added_cb, + usb_device_removed_cb removed_cb, + usb_discovery_done_cb discovery_done_cb, + void *client_data) +{ + int done; + + done = usb_host_load(context, added_cb, removed_cb, discovery_done_cb, client_data); + + while (!done) { + + done = usb_host_read_event(context); + } +} /* usb_host_run() */ struct usb_device *usb_device_open(const char *dev_name) { @@ -555,7 +647,6 @@ struct usb_request *usb_request_wait(struct usb_device *dev) { struct usbdevfs_urb *urb = NULL; struct usb_request *req = NULL; - int res; while (1) { int res = ioctl(dev->fd, USBDEVFS_REAPURB, &urb); diff --git a/logcat/event.logtags b/logcat/event.logtags index 2e814ff..6040bd9 100644 --- a/logcat/event.logtags +++ b/logcat/event.logtags @@ -134,5 +134,24 @@ 70200 aggregation (aggregation time|2|3) 70201 aggregation_test (field1|1|2),(field2|1|2),(field3|1|2),(field4|1|2),(field5|1|2) +# libc failure logging +80100 bionic_event_memcpy_buffer_overflow (uid|1) +80105 bionic_event_strcat_buffer_overflow (uid|1) +80110 bionic_event_memmov_buffer_overflow (uid|1) +80115 bionic_event_strncat_buffer_overflow (uid|1) +80120 bionic_event_strncpy_buffer_overflow (uid|1) +80125 bionic_event_memset_buffer_overflow (uid|1) +80130 bionic_event_strcpy_buffer_overflow (uid|1) + +80200 bionic_event_strcat_integer_overflow (uid|1) +80205 bionic_event_strncat_integer_overflow (uid|1) + +80300 bionic_event_resolver_old_response (uid|1) +80305 bionic_event_resolver_wrong_server (uid|1) +80310 bionic_event_resolver_wrong_query (uid|1) + +# libcore failure logging +90100 cert_pin_failure (certs|4) + # NOTE - the range 1000000-2000000 is reserved for partners and others who # want to define their own log tags without conflicting with the core platform. 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 @@ int usage(void) -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 @@ int main(int argc, char **argv) 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 cde9dee..a0c1c4f 100644 --- a/rootdir/etc/init.goldfish.rc +++ b/rootdir/etc/init.goldfish.rc @@ -5,7 +5,7 @@ on early-init symlink /mnt/sdcard /sdcard on boot - setsebool in_qemu=1 + setsebool in_qemu 1 restorecon /sys/qemu_trace/process_name restorecon /sys/qemu_trace/state restorecon /sys/qemu_trace/symbol diff --git a/rootdir/init.rc b/rootdir/init.rc index fb52486..0784c63 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -4,8 +4,8 @@ # This is a common source of Android security bugs. # -import /init.${ro.hardware}.rc import /init.usb.rc +import /init.${ro.hardware}.rc import /init.trace.rc on early-init @@ -34,6 +34,7 @@ loglevel 3 export ANDROID_ROOT /system export ANDROID_ASSETS /system/app export ANDROID_DATA /data + 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 @@ -56,8 +57,14 @@ loglevel 3 mkdir /cache 0770 system cache mkdir /config 0500 root root + # See storage config details at http://source.android.com/tech/storage/ + mkdir /mnt/shell 0700 shell shell + mkdir /storage 0050 root sdcard_r + # Directory for putting things only root should see. mkdir /mnt/secure 0700 root root + # Create private mountpoint so we can MS_MOVE from staging + mount tmpfs tmpfs /mnt/secure mode=0700,uid=0,gid=0 # Directory for staging bindmounts mkdir /mnt/secure/staging 0700 root root @@ -85,6 +92,7 @@ loglevel 3 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 @@ -113,6 +121,12 @@ loglevel 3 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 @@ -128,6 +142,9 @@ on fs on post-fs # once everything is setup, no need to modify / mount rootfs rootfs / ro remount + # mount shared so changes propagate into child namespaces + mount rootfs rootfs / shared rec + mount tmpfs tmpfs /mnt/secure private rec # We chown/chmod /cache again so because mount is run as root + defaults chown system cache /cache @@ -145,11 +162,16 @@ on post-fs chown root log /proc/vmallocinfo chmod 0440 /proc/vmallocinfo + chown root log /proc/slabinfo + chmod 0440 /proc/slabinfo + #change permissions on kmsg & sysrq-trigger so bugreports can grab kthread stacks chown root system /proc/kmsg chmod 0440 /proc/kmsg chown root system /proc/sysrq-trigger chmod 0220 /proc/sysrq-trigger + chown system log /proc/last_kmsg + chmod 0440 /proc/last_kmsg # create the lost+found directories, so as to enforce our permissions mkdir /cache/lost+found 0770 root root @@ -179,10 +201,13 @@ on post-fs-data # create basic filesystem structure mkdir /data/misc 01771 system misc - mkdir /data/misc/bluetoothd 0770 bluetooth bluetooth + mkdir /data/misc/adb 02750 system shell + mkdir /data/misc/bluedroid 0770 bluetooth net_bt_stack mkdir /data/misc/bluetooth 0770 system system mkdir /data/misc/keystore 0700 keystore keystore mkdir /data/misc/keychain 0771 system system + mkdir /data/misc/sms 0770 system radio + mkdir /data/misc/zoneinfo 0775 system system mkdir /data/misc/vpn 0770 system vpn mkdir /data/misc/systemkeys 0700 system system # give system access to wpa_supplicant.conf for backup and restore @@ -196,6 +221,7 @@ on post-fs-data mkdir /data/data 0771 system system mkdir /data/app-private 0771 system system mkdir /data/app-asec 0700 root root + mkdir /data/app-lib 0771 system system mkdir /data/app 0771 system system mkdir /data/property 0700 root root mkdir /data/ssh 0750 root shell @@ -216,6 +242,9 @@ on post-fs-data # the following directory. mkdir /data/drm 0770 drm drm + # Separate location for storing security policy files on data + mkdir /data/security 0700 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. @@ -251,6 +280,7 @@ on boot chown radio system /sys/android_power/acquire_full_wake_lock chown radio system /sys/android_power/acquire_partial_wake_lock chown radio system /sys/android_power/release_wake_lock + chown system system /sys/power/autosleep chown system system /sys/power/state chown system system /sys/power/wakeup_count chown radio system /sys/power/wake_lock @@ -274,6 +304,8 @@ on boot 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 @@ -376,6 +408,7 @@ on property:ro.debuggable=1 # adbd is controlled via property triggers in init.<platform>.usb.rc service adbd /sbin/adbd class core + socket adbd stream 660 system system disabled seclabel u:r:adbd:s0 @@ -412,12 +445,12 @@ service ril-daemon /system/bin/rild socket rild stream 660 root radio socket rild-debug stream 660 radio system user root - group radio cache inet misc audio sdcard_rw log + group radio cache inet misc audio log service surfaceflinger /system/bin/surfaceflinger class main user system - group graphics + group graphics drmrpc onrestart restart zygote service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server @@ -431,7 +464,7 @@ service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-sys service drm /system/bin/drmserver class main user drm - group drm system inet drmrpc sdcard_r + group drm system inet drmrpc service media /system/bin/mediaserver class main @@ -446,21 +479,6 @@ service bootanim /system/bin/bootanimation disabled oneshot -service dbus /system/bin/dbus-daemon --system --nofork - class main - socket dbus stream 660 bluetooth bluetooth - user bluetooth - group bluetooth net_bt_admin - -service bluetoothd /system/bin/bluetoothd -n - class main - socket bluetooth stream 660 bluetooth bluetooth - socket dbus_bluetooth stream 660 bluetooth bluetooth - # init.rc does not yet support applying capabilities, so run as root and - # let bluetoothd drop uid to bluetooth with the right linux capabilities - group bluetooth net_bt_admin misc - disabled - service installd /system/bin/installd class main socket installd stream 600 system system @@ -489,7 +507,6 @@ service keystore /system/bin/keystore /data/misc/keystore class main user keystore group keystore drmrpc - socket keystore stream 666 service dumpstate /system/bin/dumpstate -s class main diff --git a/rootdir/init.trace.rc b/rootdir/init.trace.rc index 1d114f5..8a05fd0 100644 --- a/rootdir/init.trace.rc +++ b/rootdir/init.trace.rc @@ -13,6 +13,7 @@ on boot chown root shell /sys/kernel/debug/tracing/events/sched/sched_wakeup/enable chown root shell /sys/kernel/debug/tracing/events/power/cpu_frequency/enable chown root shell /sys/kernel/debug/tracing/events/power/cpu_idle/enable + chown root shell /sys/kernel/debug/tracing/events/power/clock_set_rate/enable chown root shell /sys/kernel/debug/tracing/events/cpufreq_interactive/enable chown root shell /sys/kernel/debug/tracing/tracing_on @@ -23,6 +24,7 @@ on boot chmod 0664 /sys/kernel/debug/tracing/events/sched/sched_wakeup/enable chmod 0664 /sys/kernel/debug/tracing/events/power/cpu_frequency/enable chmod 0664 /sys/kernel/debug/tracing/events/power/cpu_idle/enable + chmod 0664 /sys/kernel/debug/tracing/events/power/clock_set_rate/enable chmod 0664 /sys/kernel/debug/tracing/events/cpufreq_interactive/enable chmod 0664 /sys/kernel/debug/tracing/tracing_on 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 @@ on property:sys.usb.config=accessory,audio_source,adb # 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/rootdir/ueventd.rc b/rootdir/ueventd.rc index c1fca00..2cf0265 100644 --- a/rootdir/ueventd.rc +++ b/rootdir/ueventd.rc @@ -27,7 +27,8 @@ /dev/android_adb 0660 adb adb /dev/android_adb_enable 0660 adb adb /dev/ttyMSM0 0600 bluetooth bluetooth -/dev/uinput 0660 system bluetooth +/dev/uhid 0660 system net_bt_stack +/dev/uinput 0660 system net_bt_stack /dev/alarm 0664 system radio /dev/tty0 0660 root system /dev/graphics/* 0660 root graphics 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 @@ include $(CLEAR_VARS) 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 @@ string_copy(char* dst, size_t dstlen, const char* src, size_t srclen) *dst++ = *src++; *dst = '\0'; /* zero-terminate result */ + return src; } /* Open 'filename' and map it into our address-space. @@ -76,13 +80,30 @@ map_file(const char* filename, size_t* filesize) 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 @@ get_package_info(const char* pkgName, PackageInfo *info) 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 @@ get_package_info(const char* pkgName, PackageInfo *info) /* 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 @@ get_package_info(const char* pkgName, PackageInfo *info) 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 @@ typedef struct { 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 @@ int main(int argc, char **argv) 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/sdcard/Android.mk b/sdcard/Android.mk index c430ac8..fb04d6d 100644 --- a/sdcard/Android.mk +++ b/sdcard/Android.mk @@ -4,6 +4,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= sdcard.c LOCAL_MODULE:= sdcard +LOCAL_CFLAGS := -Wall -Wno-unused-parameter LOCAL_SHARED_LIBRARIES := libc diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c index ad2f2ab..8d87ee9 100644 --- a/sdcard/sdcard.c +++ b/sdcard/sdcard.c @@ -25,7 +25,9 @@ #include <sys/statfs.h> #include <sys/uio.h> #include <dirent.h> +#include <limits.h> #include <ctype.h> +#include <pthread.h> #include <private/android_filesystem_config.h> @@ -40,12 +42,10 @@ * permissions at creation, owner, group, and permissions are not * changeable, symlinks and hardlinks are not createable, etc. * - * usage: sdcard <path> <uid> <gid> - * - * It must be run as root, but will change to uid/gid as soon as it - * mounts a filesystem on /storage/sdcard. It will refuse to run if uid or - * gid are zero. + * See usage() for command line options. * + * It must be run as root, but will drop to requested UID/GID as soon as it + * mounts a filesystem. It will refuse to run if requested UID/GID are zero. * * Things I believe to be true: * @@ -55,7 +55,6 @@ * - if an op that returns a fuse_entry fails writing the reply to the * kernel, you must rollback the refcount to reflect the reference the * kernel did not actually acquire - * */ #define FUSE_TRACE 0 @@ -70,30 +69,42 @@ #define FUSE_UNKNOWN_INO 0xffffffff -#define MOUNT_POINT "/storage/sdcard0" +/* Maximum number of bytes to write in one request. */ +#define MAX_WRITE (256 * 1024) + +/* Maximum number of bytes to read in one request. */ +#define MAX_READ (128 * 1024) + +/* Largest possible request. + * The request size is bounded by the maximum size of a FUSE_WRITE request because it has + * the largest possible data payload. */ +#define MAX_REQUEST_SIZE (sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in) + MAX_WRITE) + +/* Default number of threads. */ +#define DEFAULT_NUM_THREADS 2 + +/* Pseudo-error constant used to indicate that no fuse status is needed + * or that a reply has already been written. */ +#define NO_STATUS 1 struct handle { - struct node *node; int fd; }; struct dirhandle { - struct node *node; DIR *d; }; struct node { + __u32 refcount; __u64 nid; __u64 gen; struct node *next; /* per-dir sibling list */ struct node *child; /* first contained file by this dir */ - struct node *all; /* global node list */ struct node *parent; /* containing directory */ - __u32 refcount; - __u32 namelen; - + size_t namelen; char *name; /* If non-null, this is the real name of the file in the underlying storage. * This may differ from the field "name" only by case. @@ -103,95 +114,166 @@ struct node { char *actual_name; }; +/* Global data structure shared by all fuse handlers. */ struct fuse { - __u64 next_generation; - __u64 next_node_id; + pthread_mutex_t lock; + __u64 next_generation; int fd; - - struct node *all; - struct node root; - char rootpath[1024]; + char rootpath[PATH_MAX]; }; -#define PATH_BUFFER_SIZE 1024 +/* Private data used by a single fuse handler. */ +struct fuse_handler { + struct fuse* fuse; + int token; + + /* To save memory, we never use the contents of the request buffer and the read + * buffer at the same time. This allows us to share the underlying storage. */ + union { + __u8 request_buffer[MAX_REQUEST_SIZE]; + __u8 read_buffer[MAX_READ]; + }; +}; -#define NO_CASE_SENSITIVE_MATCH 0 -#define CASE_SENSITIVE_MATCH 1 +static inline void *id_to_ptr(__u64 nid) +{ + return (void *) (uintptr_t) nid; +} -/* - * Get the real-life absolute path to a node. - * node: start at this node - * buf: storage for returned string - * name: append this string to path if set +static inline __u64 ptr_to_id(void *ptr) +{ + return (__u64) (uintptr_t) ptr; +} + +static void acquire_node_locked(struct node* node) +{ + node->refcount++; + TRACE("ACQUIRE %p (%s) rc=%d\n", node, node->name, node->refcount); +} + +static void remove_node_from_parent_locked(struct node* node); + +static void release_node_locked(struct node* node) +{ + TRACE("RELEASE %p (%s) rc=%d\n", node, node->name, node->refcount); + if (node->refcount > 0) { + node->refcount--; + if (!node->refcount) { + TRACE("DESTROY %p (%s)\n", node, node->name); + remove_node_from_parent_locked(node); + + /* TODO: remove debugging - poison memory */ + memset(node->name, 0xef, node->namelen); + free(node->name); + free(node->actual_name); + memset(node, 0xfc, sizeof(*node)); + free(node); + } + } else { + ERROR("Zero refcnt %p\n", node); + } +} + +static void add_node_to_parent_locked(struct node *node, struct node *parent) { + node->parent = parent; + node->next = parent->child; + parent->child = node; + acquire_node_locked(parent); +} + +static void remove_node_from_parent_locked(struct node* node) +{ + if (node->parent) { + if (node->parent->child == node) { + node->parent->child = node->parent->child->next; + } else { + struct node *node2; + node2 = node->parent->child; + while (node2->next != node) + node2 = node2->next; + node2->next = node->next; + } + release_node_locked(node->parent); + node->parent = NULL; + node->next = NULL; + } +} + +/* Gets the absolute path to a node into the provided buffer. + * + * Populates 'buf' with the path and returns the length of the path on success, + * or returns -1 if the path is too long for the provided buffer. */ -char *do_node_get_path(struct node *node, char *buf, const char *name, int match_case_insensitive) +static ssize_t get_node_path_locked(struct node* node, char* buf, size_t bufsize) { - struct node *in_node = node; - const char *in_name = name; - char *out = buf + PATH_BUFFER_SIZE - 1; - int len; - out[0] = 0; - - if (name) { - len = strlen(name); - goto start; - } - - while (node) { - name = (node->actual_name ? node->actual_name : node->name); - len = node->namelen; - node = node->parent; - start: - if ((len + 1) > (out - buf)) - return 0; - out -= len; - memcpy(out, name, len); - /* avoid double slash at beginning of path */ - if (out[0] != '/') { - out --; - out[0] = '/'; + size_t namelen = node->namelen; + if (bufsize < namelen + 1) { + return -1; + } + + ssize_t pathlen = 0; + if (node->parent) { + pathlen = get_node_path_locked(node->parent, buf, bufsize - namelen - 2); + if (pathlen < 0) { + return -1; } + buf[pathlen++] = '/'; } - /* If we are searching for a file within node (rather than computing node's path) - * and fail, then we need to look for a case insensitive match. - */ - if (in_name && match_case_insensitive && access(out, F_OK) != 0) { - char *path, buffer[PATH_BUFFER_SIZE]; - DIR* dir; + const char* name = node->actual_name ? node->actual_name : node->name; + memcpy(buf + pathlen, name, namelen + 1); /* include trailing \0 */ + return pathlen + namelen; +} + +/* Finds the absolute path of a file within a given directory. + * Performs a case-insensitive search for the file and sets the buffer to the path + * of the first matching file. If 'search' is zero or if no match is found, sets + * the buffer to the path that the file would have, assuming the name were case-sensitive. + * + * Populates 'buf' with the path and returns the actual name (within 'buf') on success, + * or returns NULL if the path is too long for the provided buffer. + */ +static char* find_file_within(const char* path, const char* name, + char* buf, size_t bufsize, int search) +{ + size_t pathlen = strlen(path); + size_t namelen = strlen(name); + size_t childlen = pathlen + namelen + 1; + char* actual; + + if (bufsize <= childlen) { + return NULL; + } + + memcpy(buf, path, pathlen); + buf[pathlen] = '/'; + actual = buf + pathlen + 1; + memcpy(actual, name, namelen + 1); + + if (search && access(buf, F_OK)) { struct dirent* entry; - path = do_node_get_path(in_node, buffer, NULL, NO_CASE_SENSITIVE_MATCH); - dir = opendir(path); + DIR* dir = opendir(path); if (!dir) { ERROR("opendir %s failed: %s", path, strerror(errno)); - return out; + return actual; } - while ((entry = readdir(dir))) { - if (!strcasecmp(entry->d_name, in_name)) { - /* we have a match - replace the name */ - len = strlen(in_name); - memcpy(buf + PATH_BUFFER_SIZE - len - 1, entry->d_name, len); + if (!strcasecmp(entry->d_name, name)) { + /* we have a match - replace the name, don't need to copy the null again */ + memcpy(actual, entry->d_name, namelen); break; } } closedir(dir); } - - return out; + return actual; } -char *node_get_path(struct node *node, char *buf, const char *name) +static void attr_from_stat(struct fuse_attr *attr, const struct stat *s, __u64 nid) { - /* We look for case insensitive matches by default */ - return do_node_get_path(node, buf, name, CASE_SENSITIVE_MATCH); -} - -void attr_from_stat(struct fuse_attr *attr, struct stat *s) -{ - attr->ino = s->st_ino; + attr->ino = nid; attr->size = s->st_size; attr->blocks = s->st_blocks; attr->atime = s->st_atime; @@ -218,808 +300,1059 @@ void attr_from_stat(struct fuse_attr *attr, struct stat *s) attr->gid = AID_SDCARD_RW; } -int node_get_attr(struct node *node, struct fuse_attr *attr) +struct node *create_node_locked(struct fuse* fuse, + struct node *parent, const char *name, const char* actual_name) { - int res; - struct stat s; - char *path, buffer[PATH_BUFFER_SIZE]; + struct node *node; + size_t namelen = strlen(name); - path = node_get_path(node, buffer, 0); - res = lstat(path, &s); - if (res < 0) { - ERROR("lstat('%s') errno %d\n", path, errno); - return -1; + node = calloc(1, sizeof(struct node)); + if (!node) { + return NULL; } - - attr_from_stat(attr, &s); - attr->ino = node->nid; - - return 0; -} - -static void add_node_to_parent(struct node *node, struct node *parent) { - node->parent = parent; - node->next = parent->child; - parent->child = node; - parent->refcount++; + node->name = malloc(namelen + 1); + if (!node->name) { + free(node); + return NULL; + } + memcpy(node->name, name, namelen + 1); + if (strcmp(name, actual_name)) { + node->actual_name = malloc(namelen + 1); + if (!node->actual_name) { + free(node->name); + free(node); + return NULL; + } + memcpy(node->actual_name, actual_name, namelen + 1); + } + node->namelen = namelen; + node->nid = ptr_to_id(node); + node->gen = fuse->next_generation++; + acquire_node_locked(node); + add_node_to_parent_locked(node, parent); + return node; } -/* Check to see if our parent directory already has a file with a name - * that differs only by case. If we find one, store it in the actual_name - * field so node_get_path will map it to this file in the underlying storage. - */ -static void node_find_actual_name(struct node *node) +static int rename_node_locked(struct node *node, const char *name, + const char* actual_name) { - char *path, buffer[PATH_BUFFER_SIZE]; - const char *node_name = node->name; - DIR* dir; - struct dirent* entry; - - if (!node->parent) return; - - path = node_get_path(node->parent, buffer, 0); - dir = opendir(path); - if (!dir) { - ERROR("opendir %s failed: %s", path, strerror(errno)); - return; + size_t namelen = strlen(name); + int need_actual_name = strcmp(name, actual_name); + + /* make the storage bigger without actually changing the name + * in case an error occurs part way */ + if (namelen > node->namelen) { + char* new_name = realloc(node->name, namelen + 1); + if (!new_name) { + return -ENOMEM; + } + node->name = new_name; + if (need_actual_name && node->actual_name) { + char* new_actual_name = realloc(node->actual_name, namelen + 1); + if (!new_actual_name) { + return -ENOMEM; + } + node->actual_name = new_actual_name; + } } - while ((entry = readdir(dir))) { - const char *test_name = entry->d_name; - if (strcmp(test_name, node_name) && !strcasecmp(test_name, node_name)) { - /* we have a match - differs but only by case */ - node->actual_name = strdup(test_name); + /* update the name, taking care to allocate storage before overwriting the old name */ + if (need_actual_name) { + if (!node->actual_name) { + node->actual_name = malloc(namelen + 1); if (!node->actual_name) { - ERROR("strdup failed - out of memory\n"); - exit(1); + return -ENOMEM; } - break; } + memcpy(node->actual_name, actual_name, namelen + 1); + } else { + free(node->actual_name); + node->actual_name = NULL; } - closedir(dir); + memcpy(node->name, name, namelen + 1); + node->namelen = namelen; + return 0; } -struct node *node_create(struct node *parent, const char *name, __u64 nid, __u64 gen) +static struct node *lookup_node_by_id_locked(struct fuse *fuse, __u64 nid) { - struct node *node; - int namelen = strlen(name); - - node = calloc(1, sizeof(struct node)); - if (node == 0) { - return 0; - } - node->name = malloc(namelen + 1); - if (node->name == 0) { - free(node); - return 0; + if (nid == FUSE_ROOT_ID) { + return &fuse->root; + } else { + return id_to_ptr(nid); } +} - node->nid = nid; - node->gen = gen; - add_node_to_parent(node, parent); - memcpy(node->name, name, namelen + 1); - node->namelen = namelen; - node_find_actual_name(node); +static struct node* lookup_node_and_path_by_id_locked(struct fuse* fuse, __u64 nid, + char* buf, size_t bufsize) +{ + struct node* node = lookup_node_by_id_locked(fuse, nid); + if (node && get_node_path_locked(node, buf, bufsize) < 0) { + node = NULL; + } return node; } -static char *rename_node(struct node *node, const char *name) +static struct node *lookup_child_by_name_locked(struct node *node, const char *name) { - node->namelen = strlen(name); - char *newname = realloc(node->name, node->namelen + 1); - if (newname == 0) - return 0; - node->name = newname; - memcpy(node->name, name, node->namelen + 1); - node_find_actual_name(node); - return node->name; + for (node = node->child; node; node = node->next) { + /* use exact string comparison, nodes that differ by case + * must be considered distinct even if they refer to the same + * underlying file as otherwise operations such as "mv x x" + * will not work because the source and target nodes are the same. */ + if (!strcmp(name, node->name)) { + return node; + } + } + return 0; +} + +static struct node* acquire_or_create_child_locked( + struct fuse* fuse, struct node* parent, + const char* name, const char* actual_name) +{ + struct node* child = lookup_child_by_name_locked(parent, name); + if (child) { + acquire_node_locked(child); + } else { + child = create_node_locked(fuse, parent, name, actual_name); + } + return child; } -void fuse_init(struct fuse *fuse, int fd, const char *path) +static void fuse_init(struct fuse *fuse, int fd, const char *source_path) { + pthread_mutex_init(&fuse->lock, NULL); + fuse->fd = fd; - fuse->next_node_id = 2; fuse->next_generation = 0; - fuse->all = &fuse->root; - memset(&fuse->root, 0, sizeof(fuse->root)); fuse->root.nid = FUSE_ROOT_ID; /* 1 */ fuse->root.refcount = 2; - rename_node(&fuse->root, path); + fuse->root.namelen = strlen(source_path); + fuse->root.name = strdup(source_path); } -static inline void *id_to_ptr(__u64 nid) +static void fuse_status(struct fuse *fuse, __u64 unique, int err) { - return (void *) nid; + struct fuse_out_header hdr; + hdr.len = sizeof(hdr); + hdr.error = err; + hdr.unique = unique; + write(fuse->fd, &hdr, sizeof(hdr)); } -static inline __u64 ptr_to_id(void *ptr) +static void fuse_reply(struct fuse *fuse, __u64 unique, void *data, int len) { - return (__u64) ptr; + struct fuse_out_header hdr; + struct iovec vec[2]; + int res; + + hdr.len = len + sizeof(hdr); + hdr.error = 0; + hdr.unique = unique; + + vec[0].iov_base = &hdr; + vec[0].iov_len = sizeof(hdr); + vec[1].iov_base = data; + vec[1].iov_len = len; + + res = writev(fuse->fd, vec, 2); + if (res < 0) { + ERROR("*** REPLY FAILED *** %d\n", errno); + } } +static int fuse_reply_entry(struct fuse* fuse, __u64 unique, + struct node* parent, const char* name, const char* actual_name, + const char* path) +{ + struct node* node; + struct fuse_entry_out out; + struct stat s; + + if (lstat(path, &s) < 0) { + return -errno; + } + + pthread_mutex_lock(&fuse->lock); + node = acquire_or_create_child_locked(fuse, parent, name, actual_name); + if (!node) { + pthread_mutex_unlock(&fuse->lock); + return -ENOMEM; + } + memset(&out, 0, sizeof(out)); + attr_from_stat(&out.attr, &s, node->nid); + out.attr_valid = 10; + out.entry_valid = 10; + out.nodeid = node->nid; + out.generation = node->gen; + pthread_mutex_unlock(&fuse->lock); + fuse_reply(fuse, unique, &out, sizeof(out)); + return NO_STATUS; +} -struct node *lookup_by_inode(struct fuse *fuse, __u64 nid) +static int fuse_reply_attr(struct fuse* fuse, __u64 unique, __u64 nid, + const char* path) { - if (nid == FUSE_ROOT_ID) { - return &fuse->root; - } else { - return id_to_ptr(nid); + struct fuse_attr_out out; + struct stat s; + + if (lstat(path, &s) < 0) { + return -errno; } + memset(&out, 0, sizeof(out)); + attr_from_stat(&out.attr, &s, nid); + out.attr_valid = 10; + fuse_reply(fuse, unique, &out, sizeof(out)); + return NO_STATUS; } -struct node *lookup_child_by_name(struct node *node, const char *name) +static int handle_lookup(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header *hdr, const char* name) { - for (node = node->child; node; node = node->next) { - if (!strcmp(name, node->name)) { - return node; - } + struct node* parent_node; + char parent_path[PATH_MAX]; + char child_path[PATH_MAX]; + const char* actual_name; + + pthread_mutex_lock(&fuse->lock); + parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, + parent_path, sizeof(parent_path)); + TRACE("[%d] LOOKUP %s @ %llx (%s)\n", handler->token, name, hdr->nodeid, + parent_node ? parent_node->name : "?"); + pthread_mutex_unlock(&fuse->lock); + + if (!parent_node || !(actual_name = find_file_within(parent_path, name, + child_path, sizeof(child_path), 1))) { + return -ENOENT; } - return 0; + return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path); } -struct node *lookup_child_by_inode(struct node *node, __u64 nid) +static int handle_forget(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header *hdr, const struct fuse_forget_in *req) { - for (node = node->child; node; node = node->next) { - if (node->nid == nid) { - return node; + struct node* node; + + pthread_mutex_lock(&fuse->lock); + node = lookup_node_by_id_locked(fuse, hdr->nodeid); + TRACE("[%d] FORGET #%lld @ %llx (%s)\n", handler->token, req->nlookup, + hdr->nodeid, node ? node->name : "?"); + if (node) { + __u64 n = req->nlookup; + while (n--) { + release_node_locked(node); } } - return 0; + pthread_mutex_unlock(&fuse->lock); + return NO_STATUS; /* no reply */ } -static void dec_refcount(struct node *node) { - if (node->refcount > 0) { - node->refcount--; - TRACE("dec_refcount %p(%s) -> %d\n", node, node->name, node->refcount); - } else { - ERROR("Zero refcnt %p\n", node); +static int handle_getattr(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header *hdr, const struct fuse_getattr_in *req) +{ + struct node* node; + char path[PATH_MAX]; + + pthread_mutex_lock(&fuse->lock); + node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path)); + TRACE("[%d] GETATTR flags=%x fh=%llx @ %llx (%s)\n", handler->token, + req->getattr_flags, req->fh, hdr->nodeid, node ? node->name : "?"); + pthread_mutex_unlock(&fuse->lock); + + if (!node) { + return -ENOENT; } - } + return fuse_reply_attr(fuse, hdr->unique, hdr->nodeid, path); +} -static struct node *remove_child(struct node *parent, __u64 nid) +static int handle_setattr(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header *hdr, const struct fuse_setattr_in *req) { - struct node *prev = 0; - struct node *node; + struct node* node; + char path[PATH_MAX]; + struct timespec times[2]; + + pthread_mutex_lock(&fuse->lock); + node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path)); + TRACE("[%d] SETATTR fh=%llx valid=%x @ %llx (%s)\n", handler->token, + req->fh, req->valid, hdr->nodeid, node ? node->name : "?"); + pthread_mutex_unlock(&fuse->lock); + + if (!node) { + return -ENOENT; + } - for (node = parent->child; node; node = node->next) { - if (node->nid == nid) { - if (prev) { - prev->next = node->next; + /* XXX: incomplete implementation on purpose. + * chmod/chown should NEVER be implemented.*/ + + if ((req->valid & FATTR_SIZE) && truncate(path, req->size) < 0) { + return -errno; + } + + /* Handle changing atime and mtime. If FATTR_ATIME_and FATTR_ATIME_NOW + * are both set, then set it to the current time. Else, set it to the + * time specified in the request. Same goes for mtime. Use utimensat(2) + * as it allows ATIME and MTIME to be changed independently, and has + * nanosecond resolution which fuse also has. + */ + if (req->valid & (FATTR_ATIME | FATTR_MTIME)) { + times[0].tv_nsec = UTIME_OMIT; + times[1].tv_nsec = UTIME_OMIT; + if (req->valid & FATTR_ATIME) { + if (req->valid & FATTR_ATIME_NOW) { + times[0].tv_nsec = UTIME_NOW; } else { - parent->child = node->next; + times[0].tv_sec = req->atime; + times[0].tv_nsec = req->atimensec; } - node->next = 0; - node->parent = 0; - dec_refcount(parent); - return node; } - prev = node; + if (req->valid & FATTR_MTIME) { + if (req->valid & FATTR_MTIME_NOW) { + times[1].tv_nsec = UTIME_NOW; + } else { + times[1].tv_sec = req->mtime; + times[1].tv_nsec = req->mtimensec; + } + } + TRACE("[%d] Calling utimensat on %s with atime %ld, mtime=%ld\n", + handler->token, path, times[0].tv_sec, times[1].tv_sec); + if (utimensat(-1, path, times, 0) < 0) { + return -errno; + } + } + return fuse_reply_attr(fuse, hdr->unique, hdr->nodeid, path); +} + +static int handle_mknod(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_mknod_in* req, const char* name) +{ + struct node* parent_node; + char parent_path[PATH_MAX]; + char child_path[PATH_MAX]; + const char* actual_name; + + pthread_mutex_lock(&fuse->lock); + parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, + parent_path, sizeof(parent_path)); + TRACE("[%d] MKNOD %s 0%o @ %llx (%s)\n", handler->token, + name, req->mode, hdr->nodeid, parent_node ? parent_node->name : "?"); + pthread_mutex_unlock(&fuse->lock); + + if (!parent_node || !(actual_name = find_file_within(parent_path, name, + child_path, sizeof(child_path), 1))) { + return -ENOENT; + } + __u32 mode = (req->mode & (~0777)) | 0664; + if (mknod(child_path, mode, req->rdev) < 0) { + return -errno; + } + return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path); +} + +static int handle_mkdir(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_mkdir_in* req, const char* name) +{ + struct node* parent_node; + char parent_path[PATH_MAX]; + char child_path[PATH_MAX]; + const char* actual_name; + + pthread_mutex_lock(&fuse->lock); + parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, + parent_path, sizeof(parent_path)); + TRACE("[%d] MKDIR %s 0%o @ %llx (%s)\n", handler->token, + name, req->mode, hdr->nodeid, parent_node ? parent_node->name : "?"); + pthread_mutex_unlock(&fuse->lock); + + if (!parent_node || !(actual_name = find_file_within(parent_path, name, + child_path, sizeof(child_path), 1))) { + return -ENOENT; + } + __u32 mode = (req->mode & (~0777)) | 0775; + if (mkdir(child_path, mode) < 0) { + return -errno; + } + return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path); +} + +static int handle_unlink(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const char* name) +{ + struct node* parent_node; + char parent_path[PATH_MAX]; + char child_path[PATH_MAX]; + + pthread_mutex_lock(&fuse->lock); + parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, + parent_path, sizeof(parent_path)); + TRACE("[%d] UNLINK %s @ %llx (%s)\n", handler->token, + name, hdr->nodeid, parent_node ? parent_node->name : "?"); + pthread_mutex_unlock(&fuse->lock); + + if (!parent_node || !find_file_within(parent_path, name, + child_path, sizeof(child_path), 1)) { + return -ENOENT; + } + if (unlink(child_path) < 0) { + return -errno; } return 0; } -struct node *node_lookup(struct fuse *fuse, struct node *parent, const char *name, - struct fuse_attr *attr) +static int handle_rmdir(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const char* name) { + struct node* parent_node; + char parent_path[PATH_MAX]; + char child_path[PATH_MAX]; + + pthread_mutex_lock(&fuse->lock); + parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, + parent_path, sizeof(parent_path)); + TRACE("[%d] RMDIR %s @ %llx (%s)\n", handler->token, + name, hdr->nodeid, parent_node ? parent_node->name : "?"); + pthread_mutex_unlock(&fuse->lock); + + if (!parent_node || !find_file_within(parent_path, name, + child_path, sizeof(child_path), 1)) { + return -ENOENT; + } + if (rmdir(child_path) < 0) { + return -errno; + } + return 0; +} + +static int handle_rename(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_rename_in* req, + const char* old_name, const char* new_name) +{ + struct node* old_parent_node; + struct node* new_parent_node; + struct node* child_node; + char old_parent_path[PATH_MAX]; + char new_parent_path[PATH_MAX]; + char old_child_path[PATH_MAX]; + char new_child_path[PATH_MAX]; + const char* new_actual_name; int res; - struct stat s; - char *path, buffer[PATH_BUFFER_SIZE]; - struct node *node; - path = node_get_path(parent, buffer, name); - /* XXX error? */ + pthread_mutex_lock(&fuse->lock); + old_parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, + old_parent_path, sizeof(old_parent_path)); + new_parent_node = lookup_node_and_path_by_id_locked(fuse, req->newdir, + new_parent_path, sizeof(new_parent_path)); + TRACE("[%d] RENAME %s->%s @ %llx (%s) -> %llx (%s)\n", handler->token, + old_name, new_name, + hdr->nodeid, old_parent_node ? old_parent_node->name : "?", + req->newdir, new_parent_node ? new_parent_node->name : "?"); + if (!old_parent_node || !new_parent_node) { + res = -ENOENT; + goto lookup_error; + } + child_node = lookup_child_by_name_locked(old_parent_node, old_name); + if (!child_node || get_node_path_locked(child_node, + old_child_path, sizeof(old_child_path)) < 0) { + res = -ENOENT; + goto lookup_error; + } + acquire_node_locked(child_node); + pthread_mutex_unlock(&fuse->lock); - res = lstat(path, &s); - if (res < 0) - return 0; - - node = lookup_child_by_name(parent, name); - if (!node) { - node = node_create(parent, name, fuse->next_node_id++, fuse->next_generation++); - if (!node) - return 0; - node->nid = ptr_to_id(node); - node->all = fuse->all; - fuse->all = node; + /* Special case for renaming a file where destination is same path + * differing only by case. In this case we don't want to look for a case + * insensitive match. This allows commands like "mv foo FOO" to work as expected. + */ + int search = old_parent_node != new_parent_node + || strcasecmp(old_name, new_name); + if (!(new_actual_name = find_file_within(new_parent_path, new_name, + new_child_path, sizeof(new_child_path), search))) { + res = -ENOENT; + goto io_error; } - attr_from_stat(attr, &s); - attr->ino = node->nid; + TRACE("[%d] RENAME %s->%s\n", handler->token, old_child_path, new_child_path); + res = rename(old_child_path, new_child_path); + if (res < 0) { + res = -errno; + goto io_error; + } - return node; + pthread_mutex_lock(&fuse->lock); + res = rename_node_locked(child_node, new_name, new_actual_name); + if (!res) { + remove_node_from_parent_locked(child_node); + add_node_to_parent_locked(child_node, new_parent_node); + } + goto done; + +io_error: + pthread_mutex_lock(&fuse->lock); +done: + release_node_locked(child_node); +lookup_error: + pthread_mutex_unlock(&fuse->lock); + return res; } -void node_release(struct node *node) +static int handle_open(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_open_in* req) { - TRACE("RELEASE %p (%s) rc=%d\n", node, node->name, node->refcount); - dec_refcount(node); - if (node->refcount == 0) { - if (node->parent->child == node) { - node->parent->child = node->parent->child->next; - } else { - struct node *node2; + struct node* node; + char path[PATH_MAX]; + struct fuse_open_out out; + struct handle *h; - node2 = node->parent->child; - while (node2->next != node) - node2 = node2->next; - node2->next = node->next; - } + pthread_mutex_lock(&fuse->lock); + node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path)); + TRACE("[%d] OPEN 0%o @ %llx (%s)\n", handler->token, + req->flags, hdr->nodeid, node ? node->name : "?"); + pthread_mutex_unlock(&fuse->lock); - TRACE("DESTROY %p (%s)\n", node, node->name); + if (!node) { + return -ENOENT; + } + h = malloc(sizeof(*h)); + if (!h) { + return -ENOMEM; + } + TRACE("[%d] OPEN %s\n", handler->token, path); + h->fd = open(path, req->flags); + if (h->fd < 0) { + free(h); + return -errno; + } + out.fh = ptr_to_id(h); + out.open_flags = 0; + out.padding = 0; + fuse_reply(fuse, hdr->unique, &out, sizeof(out)); + return NO_STATUS; +} - node_release(node->parent); +static int handle_read(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_read_in* req) +{ + struct handle *h = id_to_ptr(req->fh); + __u64 unique = hdr->unique; + __u32 size = req->size; + __u64 offset = req->offset; + int res; - node->parent = 0; - node->next = 0; + /* Don't access any other fields of hdr or req beyond this point, the read buffer + * overlaps the request buffer and will clobber data in the request. This + * saves us 128KB per request handler thread at the cost of this scary comment. */ - /* TODO: remove debugging - poison memory */ - memset(node->name, 0xef, node->namelen); - free(node->name); - free(node->actual_name); - memset(node, 0xfc, sizeof(*node)); - free(node); + TRACE("[%d] READ %p(%d) %u@%llu\n", handler->token, + h, h->fd, size, offset); + if (size > sizeof(handler->read_buffer)) { + return -EINVAL; + } + res = pread64(h->fd, handler->read_buffer, size, offset); + if (res < 0) { + return -errno; } + fuse_reply(fuse, unique, handler->read_buffer, res); + return NO_STATUS; } -void fuse_status(struct fuse *fuse, __u64 unique, int err) +static int handle_write(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_write_in* req, + const void* buffer) { - struct fuse_out_header hdr; - hdr.len = sizeof(hdr); - hdr.error = err; - hdr.unique = unique; - if (err) { -// ERROR("*** %d ***\n", err); + struct fuse_write_out out; + struct handle *h = id_to_ptr(req->fh); + int res; + + TRACE("[%d] WRITE %p(%d) %u@%llu\n", handler->token, + h, h->fd, req->size, req->offset); + res = pwrite64(h->fd, buffer, req->size, req->offset); + if (res < 0) { + return -errno; } - write(fuse->fd, &hdr, sizeof(hdr)); + out.size = res; + fuse_reply(fuse, hdr->unique, &out, sizeof(out)); + return NO_STATUS; } -void fuse_reply(struct fuse *fuse, __u64 unique, void *data, int len) +static int handle_statfs(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr) { - struct fuse_out_header hdr; - struct iovec vec[2]; + char path[PATH_MAX]; + struct statfs stat; + struct fuse_statfs_out out; int res; - hdr.len = len + sizeof(hdr); - hdr.error = 0; - hdr.unique = unique; + pthread_mutex_lock(&fuse->lock); + TRACE("[%d] STATFS\n", handler->token); + res = get_node_path_locked(&fuse->root, path, sizeof(path)); + pthread_mutex_unlock(&fuse->lock); + if (res < 0) { + return -ENOENT; + } + if (statfs(fuse->root.name, &stat) < 0) { + return -errno; + } + memset(&out, 0, sizeof(out)); + out.st.blocks = stat.f_blocks; + out.st.bfree = stat.f_bfree; + out.st.bavail = stat.f_bavail; + out.st.files = stat.f_files; + out.st.ffree = stat.f_ffree; + out.st.bsize = stat.f_bsize; + out.st.namelen = stat.f_namelen; + out.st.frsize = stat.f_frsize; + fuse_reply(fuse, hdr->unique, &out, sizeof(out)); + return NO_STATUS; +} - vec[0].iov_base = &hdr; - vec[0].iov_len = sizeof(hdr); - vec[1].iov_base = data; - vec[1].iov_len = len; +static int handle_release(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_release_in* req) +{ + struct handle *h = id_to_ptr(req->fh); - res = writev(fuse->fd, vec, 2); + TRACE("[%d] RELEASE %p(%d)\n", handler->token, h, h->fd); + close(h->fd); + free(h); + return 0; +} + +static int handle_fsync(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_fsync_in* req) +{ + int is_data_sync = req->fsync_flags & 1; + struct handle *h = id_to_ptr(req->fh); + int res; + + TRACE("[%d] FSYNC %p(%d) is_data_sync=%d\n", handler->token, + h, h->fd, is_data_sync); + res = is_data_sync ? fdatasync(h->fd) : fsync(h->fd); if (res < 0) { - ERROR("*** REPLY FAILED *** %d\n", errno); + return -errno; } + return 0; } -void lookup_entry(struct fuse *fuse, struct node *node, - const char *name, __u64 unique) +static int handle_flush(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr) { - struct fuse_entry_out out; - - memset(&out, 0, sizeof(out)); + TRACE("[%d] FLUSH\n", handler->token); + return 0; +} + +static int handle_opendir(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_open_in* req) +{ + struct node* node; + char path[PATH_MAX]; + struct fuse_open_out out; + struct dirhandle *h; + + pthread_mutex_lock(&fuse->lock); + node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path)); + TRACE("[%d] OPENDIR @ %llx (%s)\n", handler->token, + hdr->nodeid, node ? node->name : "?"); + pthread_mutex_unlock(&fuse->lock); - node = node_lookup(fuse, node, name, &out.attr); if (!node) { - fuse_status(fuse, unique, -ENOENT); - return; + return -ENOENT; } - - node->refcount++; -// fprintf(stderr,"ACQUIRE %p (%s) rc=%d\n", node, node->name, node->refcount); - out.nodeid = node->nid; - out.generation = node->gen; - out.entry_valid = 10; - out.attr_valid = 10; - - fuse_reply(fuse, unique, &out, sizeof(out)); + h = malloc(sizeof(*h)); + if (!h) { + return -ENOMEM; + } + TRACE("[%d] OPENDIR %s\n", handler->token, path); + h->d = opendir(path); + if (!h->d) { + free(h); + return -errno; + } + out.fh = ptr_to_id(h); + fuse_reply(fuse, hdr->unique, &out, sizeof(out)); + return NO_STATUS; } -void handle_fuse_request(struct fuse *fuse, struct fuse_in_header *hdr, void *data, unsigned len) +static int handle_readdir(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_read_in* req) { - struct node *node; - - if ((len < sizeof(*hdr)) || (hdr->len != len)) { - ERROR("malformed header\n"); - return; + char buffer[8192]; + struct fuse_dirent *fde = (struct fuse_dirent*) buffer; + struct dirent *de; + struct dirhandle *h = id_to_ptr(req->fh); + + TRACE("[%d] READDIR %p\n", handler->token, h); + if (req->offset == 0) { + /* rewinddir() might have been called above us, so rewind here too */ + TRACE("[%d] calling rewinddir()\n", handler->token); + rewinddir(h->d); } + de = readdir(h->d); + if (!de) { + return 0; + } + fde->ino = FUSE_UNKNOWN_INO; + /* increment the offset so we can detect when rewinddir() seeks back to the beginning */ + fde->off = req->offset + 1; + fde->type = de->d_type; + fde->namelen = strlen(de->d_name); + memcpy(fde->name, de->d_name, fde->namelen + 1); + fuse_reply(fuse, hdr->unique, fde, + FUSE_DIRENT_ALIGN(sizeof(struct fuse_dirent) + fde->namelen)); + return NO_STATUS; +} - len -= hdr->len; +static int handle_releasedir(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_release_in* req) +{ + struct dirhandle *h = id_to_ptr(req->fh); - if (hdr->nodeid) { - node = lookup_by_inode(fuse, hdr->nodeid); - if (!node) { - fuse_status(fuse, hdr->unique, -ENOENT); - return; - } - } else { - node = 0; - } + TRACE("[%d] RELEASEDIR %p\n", handler->token, h); + closedir(h->d); + free(h); + return 0; +} +static int handle_init(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_init_in* req) +{ + struct fuse_init_out out; + + TRACE("[%d] INIT ver=%d.%d maxread=%d flags=%x\n", + handler->token, req->major, req->minor, req->max_readahead, req->flags); + out.major = FUSE_KERNEL_VERSION; + out.minor = FUSE_KERNEL_MINOR_VERSION; + out.max_readahead = req->max_readahead; + out.flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES; + out.max_background = 32; + out.congestion_threshold = 32; + out.max_write = MAX_WRITE; + fuse_reply(fuse, hdr->unique, &out, sizeof(out)); + return NO_STATUS; +} + +static int handle_fuse_request(struct fuse *fuse, struct fuse_handler* handler, + const struct fuse_in_header *hdr, const void *data, size_t data_len) +{ switch (hdr->opcode) { case FUSE_LOOKUP: { /* bytez[] -> entry_out */ - TRACE("LOOKUP %llx %s\n", hdr->nodeid, (char*) data); - lookup_entry(fuse, node, (char*) data, hdr->unique); - return; + const char* name = data; + return handle_lookup(fuse, handler, hdr, name); } + case FUSE_FORGET: { - struct fuse_forget_in *req = data; - TRACE("FORGET %llx (%s) #%lld\n", hdr->nodeid, node->name, req->nlookup); - /* no reply */ - while (req->nlookup--) - node_release(node); - return; + const struct fuse_forget_in *req = data; + return handle_forget(fuse, handler, hdr, req); } - case FUSE_GETATTR: { /* getattr_in -> attr_out */ - struct fuse_getattr_in *req = data; - struct fuse_attr_out out; - - TRACE("GETATTR flags=%x fh=%llx\n", req->getattr_flags, req->fh); - memset(&out, 0, sizeof(out)); - node_get_attr(node, &out.attr); - out.attr_valid = 10; - - fuse_reply(fuse, hdr->unique, &out, sizeof(out)); - return; + case FUSE_GETATTR: { /* getattr_in -> attr_out */ + const struct fuse_getattr_in *req = data; + return handle_getattr(fuse, handler, hdr, req); } - case FUSE_SETATTR: { /* setattr_in -> attr_out */ - struct fuse_setattr_in *req = data; - struct fuse_attr_out out; - char *path, buffer[PATH_BUFFER_SIZE]; - int res = 0; - struct timespec times[2]; - - TRACE("SETATTR fh=%llx id=%llx valid=%x\n", - req->fh, hdr->nodeid, req->valid); - - /* XXX: incomplete implementation on purpose. chmod/chown - * should NEVER be implemented.*/ - - path = node_get_path(node, buffer, 0); - if (req->valid & FATTR_SIZE) - res = truncate(path, req->size); - if (res) - goto getout; - - /* Handle changing atime and mtime. If FATTR_ATIME_and FATTR_ATIME_NOW - * are both set, then set it to the current time. Else, set it to the - * time specified in the request. Same goes for mtime. Use utimensat(2) - * as it allows ATIME and MTIME to be changed independently, and has - * nanosecond resolution which fuse also has. - */ - if (req->valid & (FATTR_ATIME | FATTR_MTIME)) { - times[0].tv_nsec = UTIME_OMIT; - times[1].tv_nsec = UTIME_OMIT; - if (req->valid & FATTR_ATIME) { - if (req->valid & FATTR_ATIME_NOW) { - times[0].tv_nsec = UTIME_NOW; - } else { - times[0].tv_sec = req->atime; - times[0].tv_nsec = req->atimensec; - } - } - if (req->valid & FATTR_MTIME) { - if (req->valid & FATTR_MTIME_NOW) { - times[1].tv_nsec = UTIME_NOW; - } else { - times[1].tv_sec = req->mtime; - times[1].tv_nsec = req->mtimensec; - } - } - TRACE("Calling utimensat on %s with atime %ld, mtime=%ld\n", path, times[0].tv_sec, times[1].tv_sec); - res = utimensat(-1, path, times, 0); - } - getout: - memset(&out, 0, sizeof(out)); - node_get_attr(node, &out.attr); - out.attr_valid = 10; - - if (res) - fuse_status(fuse, hdr->unique, -errno); - else - fuse_reply(fuse, hdr->unique, &out, sizeof(out)); - return; + case FUSE_SETATTR: { /* setattr_in -> attr_out */ + const struct fuse_setattr_in *req = data; + return handle_setattr(fuse, handler, hdr, req); } + // case FUSE_READLINK: // case FUSE_SYMLINK: case FUSE_MKNOD: { /* mknod_in, bytez[] -> entry_out */ - struct fuse_mknod_in *req = data; - char *path, buffer[PATH_BUFFER_SIZE]; - char *name = ((char*) data) + sizeof(*req); - int res; - - TRACE("MKNOD %s @ %llx\n", name, hdr->nodeid); - path = node_get_path(node, buffer, name); - - req->mode = (req->mode & (~0777)) | 0664; - res = mknod(path, req->mode, req->rdev); /* XXX perm?*/ - if (res < 0) { - fuse_status(fuse, hdr->unique, -errno); - } else { - lookup_entry(fuse, node, name, hdr->unique); - } - return; + const struct fuse_mknod_in *req = data; + const char *name = ((const char*) data) + sizeof(*req); + return handle_mknod(fuse, handler, hdr, req, name); } + case FUSE_MKDIR: { /* mkdir_in, bytez[] -> entry_out */ - struct fuse_mkdir_in *req = data; - struct fuse_entry_out out; - char *path, buffer[PATH_BUFFER_SIZE]; - char *name = ((char*) data) + sizeof(*req); - int res; - - TRACE("MKDIR %s @ %llx 0%o\n", name, hdr->nodeid, req->mode); - path = node_get_path(node, buffer, name); - - req->mode = (req->mode & (~0777)) | 0775; - res = mkdir(path, req->mode); - if (res < 0) { - fuse_status(fuse, hdr->unique, -errno); - } else { - lookup_entry(fuse, node, name, hdr->unique); - } - return; + const struct fuse_mkdir_in *req = data; + const char *name = ((const char*) data) + sizeof(*req); + return handle_mkdir(fuse, handler, hdr, req, name); } + case FUSE_UNLINK: { /* bytez[] -> */ - char *path, buffer[PATH_BUFFER_SIZE]; - int res; - TRACE("UNLINK %s @ %llx\n", (char*) data, hdr->nodeid); - path = node_get_path(node, buffer, (char*) data); - res = unlink(path); - fuse_status(fuse, hdr->unique, res ? -errno : 0); - return; + const char* name = data; + return handle_unlink(fuse, handler, hdr, name); } + case FUSE_RMDIR: { /* bytez[] -> */ - char *path, buffer[PATH_BUFFER_SIZE]; - int res; - TRACE("RMDIR %s @ %llx\n", (char*) data, hdr->nodeid); - path = node_get_path(node, buffer, (char*) data); - res = rmdir(path); - fuse_status(fuse, hdr->unique, res ? -errno : 0); - return; + const char* name = data; + return handle_rmdir(fuse, handler, hdr, name); } - case FUSE_RENAME: { /* rename_in, oldname, newname -> */ - struct fuse_rename_in *req = data; - char *oldname = ((char*) data) + sizeof(*req); - char *newname = oldname + strlen(oldname) + 1; - char *oldpath, oldbuffer[PATH_BUFFER_SIZE]; - char *newpath, newbuffer[PATH_BUFFER_SIZE]; - struct node *target; - struct node *newparent; - int res; - - TRACE("RENAME %s->%s @ %llx\n", oldname, newname, hdr->nodeid); - - target = lookup_child_by_name(node, oldname); - if (!target) { - fuse_status(fuse, hdr->unique, -ENOENT); - return; - } - oldpath = node_get_path(node, oldbuffer, oldname); - newparent = lookup_by_inode(fuse, req->newdir); - if (!newparent) { - fuse_status(fuse, hdr->unique, -ENOENT); - return; - } - if (newparent == node) { - /* Special case for renaming a file where destination - * is same path differing only by case. - * In this case we don't want to look for a case insensitive match. - * This allows commands like "mv foo FOO" to work as expected. - */ - newpath = do_node_get_path(newparent, newbuffer, newname, NO_CASE_SENSITIVE_MATCH); - } else { - newpath = node_get_path(newparent, newbuffer, newname); - } - - if (!remove_child(node, target->nid)) { - ERROR("RENAME remove_child not found"); - fuse_status(fuse, hdr->unique, -ENOENT); - return; - } - if (!rename_node(target, newname)) { - fuse_status(fuse, hdr->unique, -ENOMEM); - return; - } - add_node_to_parent(target, newparent); - - res = rename(oldpath, newpath); - TRACE("RENAME result %d\n", res); - - fuse_status(fuse, hdr->unique, res ? -errno : 0); - return; + case FUSE_RENAME: { /* rename_in, oldname, newname -> */ + const struct fuse_rename_in *req = data; + const char *old_name = ((const char*) data) + sizeof(*req); + const char *new_name = old_name + strlen(old_name) + 1; + return handle_rename(fuse, handler, hdr, req, old_name, new_name); } -// case FUSE_LINK: - case FUSE_OPEN: { /* open_in -> open_out */ - struct fuse_open_in *req = data; - struct fuse_open_out out; - char *path, buffer[PATH_BUFFER_SIZE]; - struct handle *h; - - h = malloc(sizeof(*h)); - if (!h) { - fuse_status(fuse, hdr->unique, -ENOMEM); - return; - } - path = node_get_path(node, buffer, 0); - TRACE("OPEN %llx '%s' 0%o fh=%p\n", hdr->nodeid, path, req->flags, h); - h->fd = open(path, req->flags); - if (h->fd < 0) { - ERROR("ERROR\n"); - fuse_status(fuse, hdr->unique, -errno); - free(h); - return; - } - out.fh = ptr_to_id(h); - out.open_flags = 0; - out.padding = 0; - fuse_reply(fuse, hdr->unique, &out, sizeof(out)); - return; +// case FUSE_LINK: + case FUSE_OPEN: { /* open_in -> open_out */ + const struct fuse_open_in *req = data; + return handle_open(fuse, handler, hdr, req); } + case FUSE_READ: { /* read_in -> byte[] */ - char buffer[128 * 1024]; - struct fuse_read_in *req = data; - struct handle *h = id_to_ptr(req->fh); - int res; - TRACE("READ %p(%d) %u@%llu\n", h, h->fd, req->size, req->offset); - if (req->size > sizeof(buffer)) { - fuse_status(fuse, hdr->unique, -EINVAL); - return; - } - res = pread64(h->fd, buffer, req->size, req->offset); - if (res < 0) { - fuse_status(fuse, hdr->unique, -errno); - return; - } - fuse_reply(fuse, hdr->unique, buffer, res); - return; + const struct fuse_read_in *req = data; + return handle_read(fuse, handler, hdr, req); } + case FUSE_WRITE: { /* write_in, byte[write_in.size] -> write_out */ - struct fuse_write_in *req = data; - struct fuse_write_out out; - struct handle *h = id_to_ptr(req->fh); - int res; - TRACE("WRITE %p(%d) %u@%llu\n", h, h->fd, req->size, req->offset); - res = pwrite64(h->fd, ((char*) data) + sizeof(*req), req->size, req->offset); - if (res < 0) { - fuse_status(fuse, hdr->unique, -errno); - return; - } - out.size = res; - fuse_reply(fuse, hdr->unique, &out, sizeof(out)); - goto oops; + const struct fuse_write_in *req = data; + const void* buffer = (const __u8*)data + sizeof(*req); + return handle_write(fuse, handler, hdr, req, buffer); } - case FUSE_STATFS: { /* getattr_in -> attr_out */ - struct statfs stat; - struct fuse_statfs_out out; - int res; - - TRACE("STATFS\n"); - - if (statfs(fuse->root.name, &stat)) { - fuse_status(fuse, hdr->unique, -errno); - return; - } - memset(&out, 0, sizeof(out)); - out.st.blocks = stat.f_blocks; - out.st.bfree = stat.f_bfree; - out.st.bavail = stat.f_bavail; - out.st.files = stat.f_files; - out.st.ffree = stat.f_ffree; - out.st.bsize = stat.f_bsize; - out.st.namelen = stat.f_namelen; - out.st.frsize = stat.f_frsize; - fuse_reply(fuse, hdr->unique, &out, sizeof(out)); - return; + case FUSE_STATFS: { /* getattr_in -> attr_out */ + return handle_statfs(fuse, handler, hdr); } + case FUSE_RELEASE: { /* release_in -> */ - struct fuse_release_in *req = data; - struct handle *h = id_to_ptr(req->fh); - TRACE("RELEASE %p(%d)\n", h, h->fd); - close(h->fd); - free(h); - fuse_status(fuse, hdr->unique, 0); - return; + const struct fuse_release_in *req = data; + return handle_release(fuse, handler, hdr, req); } -// case FUSE_FSYNC: + + case FUSE_FSYNC: { + const struct fuse_fsync_in *req = data; + return handle_fsync(fuse, handler, hdr, req); + } + // case FUSE_SETXATTR: // case FUSE_GETXATTR: // case FUSE_LISTXATTR: // case FUSE_REMOVEXATTR: - case FUSE_FLUSH: - fuse_status(fuse, hdr->unique, 0); - return; - case FUSE_OPENDIR: { /* open_in -> open_out */ - struct fuse_open_in *req = data; - struct fuse_open_out out; - char *path, buffer[PATH_BUFFER_SIZE]; - struct dirhandle *h; - - h = malloc(sizeof(*h)); - if (!h) { - fuse_status(fuse, hdr->unique, -ENOMEM); - return; - } + case FUSE_FLUSH: { + return handle_flush(fuse, handler, hdr); + } - path = node_get_path(node, buffer, 0); - TRACE("OPENDIR %llx '%s'\n", hdr->nodeid, path); - h->d = opendir(path); - if (h->d == 0) { - ERROR("ERROR\n"); - fuse_status(fuse, hdr->unique, -errno); - free(h); - return; - } - out.fh = ptr_to_id(h); - fuse_reply(fuse, hdr->unique, &out, sizeof(out)); - return; + case FUSE_OPENDIR: { /* open_in -> open_out */ + const struct fuse_open_in *req = data; + return handle_opendir(fuse, handler, hdr, req); } + case FUSE_READDIR: { - struct fuse_read_in *req = data; - char buffer[8192]; - struct fuse_dirent *fde = (struct fuse_dirent*) buffer; - struct dirent *de; - struct dirhandle *h = id_to_ptr(req->fh); - TRACE("READDIR %p\n", h); - if (req->offset == 0) { - /* rewinddir() might have been called above us, so rewind here too */ - TRACE("calling rewinddir()\n"); - rewinddir(h->d); - } - de = readdir(h->d); - if (!de) { - fuse_status(fuse, hdr->unique, 0); - return; - } - fde->ino = FUSE_UNKNOWN_INO; - /* increment the offset so we can detect when rewinddir() seeks back to the beginning */ - fde->off = req->offset + 1; - fde->type = de->d_type; - fde->namelen = strlen(de->d_name); - memcpy(fde->name, de->d_name, fde->namelen + 1); - fuse_reply(fuse, hdr->unique, fde, - FUSE_DIRENT_ALIGN(sizeof(struct fuse_dirent) + fde->namelen)); - return; + const struct fuse_read_in *req = data; + return handle_readdir(fuse, handler, hdr, req); } + case FUSE_RELEASEDIR: { /* release_in -> */ - struct fuse_release_in *req = data; - struct dirhandle *h = id_to_ptr(req->fh); - TRACE("RELEASEDIR %p\n",h); - closedir(h->d); - free(h); - fuse_status(fuse, hdr->unique, 0); - return; + const struct fuse_release_in *req = data; + return handle_releasedir(fuse, handler, hdr, req); } + // case FUSE_FSYNCDIR: case FUSE_INIT: { /* init_in -> init_out */ - struct fuse_init_in *req = data; - struct fuse_init_out out; - - TRACE("INIT ver=%d.%d maxread=%d flags=%x\n", - req->major, req->minor, req->max_readahead, req->flags); - - out.major = FUSE_KERNEL_VERSION; - out.minor = FUSE_KERNEL_MINOR_VERSION; - out.max_readahead = req->max_readahead; - out.flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES; - out.max_background = 32; - out.congestion_threshold = 32; - out.max_write = 256 * 1024; - - fuse_reply(fuse, hdr->unique, &out, sizeof(out)); - return; + const struct fuse_init_in *req = data; + return handle_init(fuse, handler, hdr, req); } + default: { - struct fuse_out_header h; - ERROR("NOTIMPL op=%d uniq=%llx nid=%llx\n", - hdr->opcode, hdr->unique, hdr->nodeid); - - oops: - h.len = sizeof(h); - h.error = -ENOSYS; - h.unique = hdr->unique; - write(fuse->fd, &h, sizeof(h)); - break; - } - } + TRACE("[%d] NOTIMPL op=%d uniq=%llx nid=%llx\n", + handler->token, hdr->opcode, hdr->unique, hdr->nodeid); + return -ENOSYS; + } + } } -void handle_fuse_requests(struct fuse *fuse) +static void handle_fuse_requests(struct fuse_handler* handler) { - unsigned char req[256 * 1024 + 128]; - int len; - + struct fuse* fuse = handler->fuse; for (;;) { - len = read(fuse->fd, req, sizeof(req)); + ssize_t len = read(fuse->fd, + handler->request_buffer, sizeof(handler->request_buffer)); if (len < 0) { - if (errno == EINTR) - continue; - ERROR("handle_fuse_requests: errno=%d\n", errno); - return; + if (errno != EINTR) { + ERROR("[%d] handle_fuse_requests: errno=%d\n", handler->token, errno); + } + continue; + } + + if ((size_t)len < sizeof(struct fuse_in_header)) { + ERROR("[%d] request too short: len=%zu\n", handler->token, (size_t)len); + continue; + } + + const struct fuse_in_header *hdr = (void*)handler->request_buffer; + if (hdr->len != (size_t)len) { + ERROR("[%d] malformed header: len=%zu, hdr->len=%u\n", + handler->token, (size_t)len, hdr->len); + continue; + } + + const void *data = handler->request_buffer + sizeof(struct fuse_in_header); + size_t data_len = len - sizeof(struct fuse_in_header); + __u64 unique = hdr->unique; + int res = handle_fuse_request(fuse, handler, hdr, data, data_len); + + /* We do not access the request again after this point because the underlying + * buffer storage may have been reused while processing the request. */ + + if (res != NO_STATUS) { + if (res) { + TRACE("[%d] ERROR %d\n", handler->token, res); + } + fuse_status(fuse, unique, res); } - handle_fuse_request(fuse, (void*) req, (void*) (req + sizeof(struct fuse_in_header)), len); } } -static int usage() +static void* start_handler(void* data) { - ERROR("usage: sdcard <path> <uid> <gid>\n"); - return -1; + struct fuse_handler* handler = data; + handle_fuse_requests(handler); + return NULL; } -int main(int argc, char **argv) +static int ignite_fuse(struct fuse* fuse, int num_threads) { - struct fuse fuse; - char opts[256]; - int fd; - int res; - const char *path = NULL; + struct fuse_handler* handlers; int i; - unsigned int uid = 0; - unsigned int gid = 0; - - if (argc != 4) { - return usage(); + handlers = malloc(num_threads * sizeof(struct fuse_handler)); + if (!handlers) { + ERROR("cannot allocate storage for threads"); + return -ENOMEM; } - path = argv[1]; - - char* endptr = NULL; - errno = 0; - uid = strtoul(argv[2], &endptr, 10); - if (*endptr != '\0' || errno != 0) { - ERROR("Invalid uid"); - return usage(); + for (i = 0; i < num_threads; i++) { + handlers[i].fuse = fuse; + handlers[i].token = i; } - endptr = NULL; - errno = 0; - gid = strtoul(argv[3], &endptr, 10); - if (*endptr != '\0' || errno != 0) { - ERROR("Invalid gid"); - return usage(); + for (i = 1; i < num_threads; i++) { + pthread_t thread; + int res = pthread_create(&thread, NULL, start_handler, &handlers[i]); + if (res) { + ERROR("failed to start thread #%d, error=%d", i, res); + goto quit; + } } + handle_fuse_requests(&handlers[0]); + ERROR("terminated prematurely"); + + /* don't bother killing all of the other threads or freeing anything, + * should never get here anyhow */ +quit: + exit(1); +} + +static int usage() +{ + ERROR("usage: sdcard [-t<threads>] <source_path> <dest_path> <uid> <gid>\n" + " -t<threads>: specify number of threads to use, default -t%d\n" + "\n", DEFAULT_NUM_THREADS); + return 1; +} + +static int run(const char* source_path, const char* dest_path, uid_t uid, gid_t gid, + int num_threads) { + int fd; + char opts[256]; + int res; + struct fuse fuse; - /* cleanup from previous instance, if necessary */ - umount2(MOUNT_POINT, 2); + /* cleanup from previous instance, if necessary */ + umount2(dest_path, 2); fd = open("/dev/fuse", O_RDWR); if (fd < 0){ - ERROR("cannot open fuse device (%d)\n", errno); + ERROR("cannot open fuse device (error %d)\n", errno); return -1; } - sprintf(opts, "fd=%i,rootmode=40000,default_permissions,allow_other," - "user_id=%d,group_id=%d", fd, uid, gid); - - res = mount("/dev/fuse", MOUNT_POINT, "fuse", MS_NOSUID | MS_NODEV, opts); + snprintf(opts, sizeof(opts), + "fd=%i,rootmode=40000,default_permissions,allow_other,user_id=%d,group_id=%d", + fd, uid, gid); + + res = mount("/dev/fuse", dest_path, "fuse", MS_NOSUID | MS_NODEV, opts); if (res < 0) { - ERROR("cannot mount fuse filesystem (%d)\n", errno); - return -1; + ERROR("cannot mount fuse filesystem (error %d)\n", errno); + goto error; } - if (setgid(gid) < 0) { - ERROR("cannot setgid!\n"); - return -1; + res = setgid(gid); + if (res < 0) { + ERROR("cannot setgid (error %d)\n", errno); + goto error; } - if (setuid(uid) < 0) { - ERROR("cannot setuid!\n"); - return -1; + + res = setuid(uid); + if (res < 0) { + ERROR("cannot setuid (error %d)\n", errno); + goto error; } - fuse_init(&fuse, fd, path); + fuse_init(&fuse, fd, source_path); umask(0); - handle_fuse_requests(&fuse); - - return 0; + res = ignite_fuse(&fuse, num_threads); + + /* we do not attempt to umount the file system here because we are no longer + * running as the root user */ + +error: + close(fd); + return res; +} + +int main(int argc, char **argv) +{ + int res; + const char *source_path = NULL; + const char *dest_path = NULL; + uid_t uid = 0; + gid_t gid = 0; + int num_threads = DEFAULT_NUM_THREADS; + int i; + + for (i = 1; i < argc; i++) { + char* arg = argv[i]; + if (!strncmp(arg, "-t", 2)) + num_threads = strtoul(arg + 2, 0, 10); + else if (!source_path) + source_path = arg; + else if (!dest_path) + dest_path = arg; + else if (!uid) { + char* endptr = NULL; + errno = 0; + uid = strtoul(arg, &endptr, 10); + if (*endptr != '\0' || errno != 0) { + ERROR("Invalid uid"); + return usage(); + } + } else if (!gid) { + char* endptr = NULL; + errno = 0; + gid = strtoul(arg, &endptr, 10); + if (*endptr != '\0' || errno != 0) { + ERROR("Invalid gid"); + return usage(); + } + } else { + ERROR("too many arguments\n"); + return usage(); + } + } + + if (!source_path) { + ERROR("no source path specified\n"); + return usage(); + } + if (!dest_path) { + ERROR("no dest path specified\n"); + return usage(); + } + if (!uid || !gid) { + ERROR("uid and gid must be nonzero\n"); + return usage(); + } + if (num_threads < 1) { + ERROR("number of threads must be at least 1\n"); + return usage(); + } + + res = run(source_path, dest_path, uid, gid, num_threads); + return res < 0 ? 1 : 0; } diff --git a/toolbox/Android.mk b/toolbox/Android.mk index be95e7c..dbbce06 100644 --- a/toolbox/Android.mk +++ b/toolbox/Android.mk @@ -56,11 +56,8 @@ TOOLS := \ ionice \ touch \ lsof \ - md5 - -ifeq ($(HAVE_SELINUX),true) - -TOOLS += \ + du \ + md5 \ getenforce \ setenforce \ chcon \ @@ -70,31 +67,31 @@ TOOLS += \ setsebool \ load_policy -endif - - ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) TOOLS += r endif -LOCAL_SRC_FILES:= \ +ALL_TOOLS = $(TOOLS) +ALL_TOOLS += \ + cp \ + grep + +LOCAL_SRC_FILES := \ dynarray.c \ toolbox.c \ - $(patsubst %,%.c,$(TOOLS)) - -LOCAL_SHARED_LIBRARIES := libcutils libc libusbhost + $(patsubst %,%.c,$(TOOLS)) \ + cp/cp.c cp/utils.c \ + grep/grep.c grep/fastgrep.c grep/file.c grep/queue.c grep/util.c LOCAL_C_INCLUDES := bionic/libc/bionic -ifeq ($(HAVE_SELINUX),true) - -LOCAL_CFLAGS += -DHAVE_SELINUX -LOCAL_SHARED_LIBRARIES += libselinux -LOCAL_C_INCLUDES += external/libselinux/include - -endif +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libc \ + libusbhost \ + libselinux -LOCAL_MODULE:= toolbox +LOCAL_MODULE := toolbox # Including this will define $(intermediates). # @@ -103,7 +100,7 @@ include $(BUILD_EXECUTABLE) $(LOCAL_PATH)/toolbox.c: $(intermediates)/tools.h TOOLS_H := $(intermediates)/tools.h -$(TOOLS_H): PRIVATE_TOOLS := $(TOOLS) +$(TOOLS_H): PRIVATE_TOOLS := $(ALL_TOOLS) $(TOOLS_H): PRIVATE_CUSTOM_TOOL = echo "/* file generated automatically */" > $@ ; for t in $(PRIVATE_TOOLS) ; do echo "TOOL($$t)" >> $@ ; done $(TOOLS_H): $(LOCAL_PATH)/Android.mk $(TOOLS_H): @@ -111,7 +108,7 @@ $(TOOLS_H): # Make #!/system/bin/toolbox launchers for each tool. # -SYMLINKS := $(addprefix $(TARGET_OUT)/bin/,$(TOOLS)) +SYMLINKS := $(addprefix $(TARGET_OUT)/bin/,$(ALL_TOOLS)) $(SYMLINKS): TOOLBOX_BINARY := $(LOCAL_MODULE) $(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk @echo "Symlink: $@ -> $(TOOLBOX_BINARY)" diff --git a/toolbox/cp/cp.c b/toolbox/cp/cp.c new file mode 100644 index 0000000..bd3c70e --- /dev/null +++ b/toolbox/cp/cp.c @@ -0,0 +1,552 @@ +/* $NetBSD: cp.c,v 1.58 2012/01/04 15:58:37 christos Exp $ */ + +/* + * Copyright (c) 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * David Hitz of Auspex Systems Inc. + * + * 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 University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT( +"@(#) Copyright (c) 1988, 1993, 1994\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)cp.c 8.5 (Berkeley) 4/29/95"; +#else +__RCSID("$NetBSD: cp.c,v 1.58 2012/01/04 15:58:37 christos Exp $"); +#endif +#endif /* not lint */ + +/* + * Cp copies source files to target files. + * + * The global PATH_T structure "to" always contains the path to the + * current target file. Since fts(3) does not change directories, + * this path can be either absolute or dot-relative. + * + * The basic algorithm is to initialize "to" and use fts(3) to traverse + * the file hierarchy rooted in the argument list. A trivial case is the + * case of 'cp file1 file2'. The more interesting case is the case of + * 'cp file1 file2 ... fileN dir' where the hierarchy is traversed and the + * path (relative to the root of the traversal) is appended to dir (stored + * in "to") to form the final target path. + */ + +#include <sys/param.h> +#include <sys/stat.h> + +#include <assert.h> +#include <err.h> +#include <errno.h> +#include <fts.h> +#include <locale.h> +#include <signal.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "extern.h" + +#define STRIP_TRAILING_SLASH(p) { \ + while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/') \ + *--(p).p_end = '\0'; \ +} + +static char empty[] = ""; +PATH_T to = { .p_end = to.p_path, .target_end = empty }; + +uid_t myuid; +int Hflag, Lflag, Rflag, Pflag, fflag, iflag, lflag, pflag, rflag, vflag, Nflag; +mode_t myumask; +sig_atomic_t pinfo; + +enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE }; + +static int copy(char *[], enum op, int); + +static void +progress(int sig __unused) +{ + + pinfo++; +} + +int +cp_main(int argc, char *argv[]) +{ + struct stat to_stat, tmp_stat; + enum op type; + int ch, fts_options, r, have_trailing_slash; + char *target, **src; + +#ifndef ANDROID + setprogname(argv[0]); +#endif + (void)setlocale(LC_ALL, ""); + + Hflag = Lflag = Pflag = Rflag = 0; + while ((ch = getopt(argc, argv, "HLNPRfailprv")) != -1) + switch (ch) { + case 'H': + Hflag = 1; + Lflag = Pflag = 0; + break; + case 'L': + Lflag = 1; + Hflag = Pflag = 0; + break; + case 'N': + Nflag = 1; + break; + case 'P': + Pflag = 1; + Hflag = Lflag = 0; + break; + case 'R': + Rflag = 1; + break; + case 'a': + Pflag = 1; + pflag = 1; + Rflag = 1; + Hflag = Lflag = 0; + break; + case 'f': + fflag = 1; + iflag = 0; + break; + case 'i': + iflag = isatty(fileno(stdin)); + fflag = 0; + break; + case 'l': + lflag = 1; + break; + case 'p': + pflag = 1; + break; + case 'r': + rflag = 1; + break; + case 'v': + vflag = 1; + break; + case '?': + default: + cp_usage(); + /* NOTREACHED */ + } + argc -= optind; + argv += optind; + + if (argc < 2) + cp_usage(); + + fts_options = FTS_NOCHDIR | FTS_PHYSICAL; + if (rflag) { + if (Rflag) { + errx(EXIT_FAILURE, + "the -R and -r options may not be specified together."); + /* NOTREACHED */ + } + if (Hflag || Lflag || Pflag) { + errx(EXIT_FAILURE, + "the -H, -L, and -P options may not be specified with the -r option."); + /* NOTREACHED */ + } + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + } + + if (Rflag) { + if (Hflag) + fts_options |= FTS_COMFOLLOW; + if (Lflag) { + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + } + } else if (!Pflag) { + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL | FTS_COMFOLLOW; + } + + myuid = getuid(); + + /* Copy the umask for explicit mode setting. */ + myumask = umask(0); + (void)umask(myumask); + + /* Save the target base in "to". */ + target = argv[--argc]; + if (strlcpy(to.p_path, target, sizeof(to.p_path)) >= sizeof(to.p_path)) + errx(EXIT_FAILURE, "%s: name too long", target); + to.p_end = to.p_path + strlen(to.p_path); + have_trailing_slash = (to.p_end[-1] == '/'); + if (have_trailing_slash) + STRIP_TRAILING_SLASH(to); + to.target_end = to.p_end; + + /* Set end of argument list for fts(3). */ + argv[argc] = NULL; + +#ifndef ANDROID + (void)signal(SIGINFO, progress); +#endif + + /* + * Cp has two distinct cases: + * + * cp [-R] source target + * cp [-R] source1 ... sourceN directory + * + * In both cases, source can be either a file or a directory. + * + * In (1), the target becomes a copy of the source. That is, if the + * source is a file, the target will be a file, and likewise for + * directories. + * + * In (2), the real target is not directory, but "directory/source". + */ + if (Pflag) + r = lstat(to.p_path, &to_stat); + else + r = stat(to.p_path, &to_stat); + if (r == -1 && errno != ENOENT) { + err(EXIT_FAILURE, "%s", to.p_path); + /* NOTREACHED */ + } + if (r == -1 || !S_ISDIR(to_stat.st_mode)) { + /* + * Case (1). Target is not a directory. + */ + if (argc > 1) + cp_usage(); + /* + * Need to detect the case: + * cp -R dir foo + * Where dir is a directory and foo does not exist, where + * we want pathname concatenations turned on but not for + * the initial mkdir(). + */ + if (r == -1) { + if (rflag || (Rflag && (Lflag || Hflag))) + r = stat(*argv, &tmp_stat); + else + r = lstat(*argv, &tmp_stat); + if (r == -1) { + err(EXIT_FAILURE, "%s", *argv); + /* NOTREACHED */ + } + + if (S_ISDIR(tmp_stat.st_mode) && (Rflag || rflag)) + type = DIR_TO_DNE; + else + type = FILE_TO_FILE; + } else + type = FILE_TO_FILE; + + if (have_trailing_slash && type == FILE_TO_FILE) { + if (r == -1) + errx(1, "directory %s does not exist", + to.p_path); + else + errx(1, "%s is not a directory", to.p_path); + } + } else { + /* + * Case (2). Target is a directory. + */ + type = FILE_TO_DIR; + } + + /* + * make "cp -rp src/ dst" behave like "cp -rp src dst" not + * like "cp -rp src/. dst" + */ + for (src = argv; *src; src++) { + size_t len = strlen(*src); + while (len-- > 1 && (*src)[len] == '/') + (*src)[len] = '\0'; + } + + exit(copy(argv, type, fts_options)); + /* NOTREACHED */ +} + +static int dnestack[MAXPATHLEN]; /* unlikely we'll have more nested dirs */ +static ssize_t dnesp; +static void +pushdne(int dne) +{ + + dnestack[dnesp++] = dne; + assert(dnesp < MAXPATHLEN); +} + +static int +popdne(void) +{ + int rv; + + rv = dnestack[--dnesp]; + assert(dnesp >= 0); + return rv; +} + +static int +copy(char *argv[], enum op type, int fts_options) +{ + struct stat to_stat; + FTS *ftsp; + FTSENT *curr; + int base, dne, sval; + int this_failed, any_failed; + size_t nlen; + char *p, *target_mid; + + base = 0; /* XXX gcc -Wuninitialized (see comment below) */ + + if ((ftsp = fts_open(argv, fts_options, NULL)) == NULL) + err(EXIT_FAILURE, "%s", argv[0]); + /* NOTREACHED */ + for (any_failed = 0; (curr = fts_read(ftsp)) != NULL;) { + this_failed = 0; + switch (curr->fts_info) { + case FTS_NS: + case FTS_DNR: + case FTS_ERR: + warnx("%s: %s", curr->fts_path, + strerror(curr->fts_errno)); + this_failed = any_failed = 1; + continue; + case FTS_DC: /* Warn, continue. */ + warnx("%s: directory causes a cycle", curr->fts_path); + this_failed = any_failed = 1; + continue; + } + + /* + * If we are in case (2) or (3) above, we need to append the + * source name to the target name. + */ + if (type != FILE_TO_FILE) { + if ((curr->fts_namelen + + to.target_end - to.p_path + 1) > MAXPATHLEN) { + warnx("%s/%s: name too long (not copied)", + to.p_path, curr->fts_name); + this_failed = any_failed = 1; + continue; + } + + /* + * Need to remember the roots of traversals to create + * correct pathnames. If there's a directory being + * copied to a non-existent directory, e.g. + * cp -R a/dir noexist + * the resulting path name should be noexist/foo, not + * noexist/dir/foo (where foo is a file in dir), which + * is the case where the target exists. + * + * Also, check for "..". This is for correct path + * concatentation for paths ending in "..", e.g. + * cp -R .. /tmp + * Paths ending in ".." are changed to ".". This is + * tricky, but seems the easiest way to fix the problem. + * + * XXX + * Since the first level MUST be FTS_ROOTLEVEL, base + * is always initialized. + */ + if (curr->fts_level == FTS_ROOTLEVEL) { + if (type != DIR_TO_DNE) { + p = strrchr(curr->fts_path, '/'); + base = (p == NULL) ? 0 : + (int)(p - curr->fts_path + 1); + + if (!strcmp(&curr->fts_path[base], + "..")) + base += 1; + } else + base = curr->fts_pathlen; + } + + p = &curr->fts_path[base]; + nlen = curr->fts_pathlen - base; + target_mid = to.target_end; + if (*p != '/' && target_mid[-1] != '/') + *target_mid++ = '/'; + *target_mid = 0; + + if (target_mid - to.p_path + nlen >= PATH_MAX) { + warnx("%s%s: name too long (not copied)", + to.p_path, p); + this_failed = any_failed = 1; + continue; + } + (void)strncat(target_mid, p, nlen); + to.p_end = target_mid + nlen; + *to.p_end = 0; + STRIP_TRAILING_SLASH(to); + } + + sval = Pflag ? lstat(to.p_path, &to_stat) : stat(to.p_path, &to_stat); + /* Not an error but need to remember it happened */ + if (sval == -1) + dne = 1; + else { + if (to_stat.st_dev == curr->fts_statp->st_dev && + to_stat.st_ino == curr->fts_statp->st_ino) { + warnx("%s and %s are identical (not copied).", + to.p_path, curr->fts_path); + this_failed = any_failed = 1; + if (S_ISDIR(curr->fts_statp->st_mode)) + (void)fts_set(ftsp, curr, FTS_SKIP); + continue; + } + if (!S_ISDIR(curr->fts_statp->st_mode) && + S_ISDIR(to_stat.st_mode)) { + warnx("cannot overwrite directory %s with non-directory %s", + to.p_path, curr->fts_path); + this_failed = any_failed = 1; + continue; + } + dne = 0; + } + + switch (curr->fts_statp->st_mode & S_IFMT) { + case S_IFLNK: + /* Catch special case of a non dangling symlink */ + if((fts_options & FTS_LOGICAL) || + ((fts_options & FTS_COMFOLLOW) && curr->fts_level == 0)) { + if (copy_file(curr, dne)) + this_failed = any_failed = 1; + } else { + if (copy_link(curr, !dne)) + this_failed = any_failed = 1; + } + break; + case S_IFDIR: + if (!Rflag && !rflag) { + if (curr->fts_info == FTS_D) + warnx("%s is a directory (not copied).", + curr->fts_path); + (void)fts_set(ftsp, curr, FTS_SKIP); + this_failed = any_failed = 1; + break; + } + + /* + * Directories get noticed twice: + * In the first pass, create it if needed. + * In the second pass, after the children have been copied, set the permissions. + */ + if (curr->fts_info == FTS_D) /* First pass */ + { + /* + * If the directory doesn't exist, create the new + * one with the from file mode plus owner RWX bits, + * modified by the umask. Trade-off between being + * able to write the directory (if from directory is + * 555) and not causing a permissions race. If the + * umask blocks owner writes, we fail.. + */ + pushdne(dne); + if (dne) { + if (mkdir(to.p_path, + curr->fts_statp->st_mode | S_IRWXU) < 0) + err(EXIT_FAILURE, "%s", + to.p_path); + /* NOTREACHED */ + } else if (!S_ISDIR(to_stat.st_mode)) { + errno = ENOTDIR; + err(EXIT_FAILURE, "%s", + to.p_path); + /* NOTREACHED */ + } + } + else if (curr->fts_info == FTS_DP) /* Second pass */ + { + /* + * If not -p and directory didn't exist, set it to be + * the same as the from directory, umodified by the + * umask; arguably wrong, but it's been that way + * forever. + */ + if (pflag && setfile(curr->fts_statp, 0)) + this_failed = any_failed = 1; + else if ((dne = popdne())) + (void)chmod(to.p_path, + curr->fts_statp->st_mode); + } + else + { + warnx("directory %s encountered when not expected.", + curr->fts_path); + this_failed = any_failed = 1; + break; + } + + break; + case S_IFBLK: + case S_IFCHR: + if (Rflag) { + if (copy_special(curr->fts_statp, !dne)) + this_failed = any_failed = 1; + } else + if (copy_file(curr, dne)) + this_failed = any_failed = 1; + break; + case S_IFIFO: + if (Rflag) { + if (copy_fifo(curr->fts_statp, !dne)) + this_failed = any_failed = 1; + } else + if (copy_file(curr, dne)) + this_failed = any_failed = 1; + break; + default: + if (copy_file(curr, dne)) + this_failed = any_failed = 1; + break; + } + if (vflag && !this_failed) + (void)printf("%s -> %s\n", curr->fts_path, to.p_path); + } + if (errno) { + err(EXIT_FAILURE, "fts_read"); + /* NOTREACHED */ + } + (void)fts_close(ftsp); + return (any_failed); +} diff --git a/toolbox/cp/extern.h b/toolbox/cp/extern.h new file mode 100644 index 0000000..ffbadf7 --- /dev/null +++ b/toolbox/cp/extern.h @@ -0,0 +1,61 @@ +/* $NetBSD: extern.h,v 1.17 2012/01/04 15:58:37 christos Exp $ */ + +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * 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 University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.2 (Berkeley) 4/1/94 + */ + +#ifndef _EXTERN_H_ +#define _EXTERN_H_ + +typedef struct { + char *p_end; /* pointer to NULL at end of path */ + char *target_end; /* pointer to end of target base */ + char p_path[MAXPATHLEN + 1]; /* pointer to the start of a path */ +} PATH_T; + +extern PATH_T to; +extern uid_t myuid; +extern int Rflag, rflag, Hflag, Lflag, Pflag, fflag, iflag, lflag, pflag, Nflag; +extern mode_t myumask; +extern sig_atomic_t pinfo; + +#include <sys/cdefs.h> + +__BEGIN_DECLS +int copy_fifo(struct stat *, int); +int copy_file(FTSENT *, int); +int copy_link(FTSENT *, int); +int copy_special(struct stat *, int); +int set_utimes(const char *, struct stat *); +int setfile(struct stat *, int); +void cp_usage(void) __attribute__((__noreturn__)); +__END_DECLS + +#endif /* !_EXTERN_H_ */ diff --git a/toolbox/cp/utils.c b/toolbox/cp/utils.c new file mode 100644 index 0000000..b682bbe --- /dev/null +++ b/toolbox/cp/utils.c @@ -0,0 +1,446 @@ +/* $NetBSD: utils.c,v 1.41 2012/01/04 15:58:37 christos Exp $ */ + +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * 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 University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)utils.c 8.3 (Berkeley) 4/1/94"; +#else +__RCSID("$NetBSD: utils.c,v 1.41 2012/01/04 15:58:37 christos Exp $"); +#endif +#endif /* not lint */ + +#include <sys/mman.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/time.h> +#ifndef ANDROID +#include <sys/extattr.h> +#endif + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <fts.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "extern.h" + +#ifdef ANDROID +#define MAXBSIZE 65536 +#endif + +#define MMAP_MAX_SIZE (8 * 1048576) +#define MMAP_MAX_WRITE (64 * 1024) + +int +set_utimes(const char *file, struct stat *fs) +{ + static struct timeval tv[2]; + +#ifdef ANDROID + tv[0].tv_sec = fs->st_atime; + tv[0].tv_usec = 0; + tv[1].tv_sec = fs->st_mtime; + tv[1].tv_usec = 0; + + if (utimes(file, tv)) { + warn("utimes: %s", file); + return 1; + } +#else + TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec); + TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec); + + if (lutimes(file, tv)) { + warn("lutimes: %s", file); + return (1); + } +#endif + return (0); +} + +struct finfo { + const char *from; + const char *to; + size_t size; +}; + +static void +progress(const struct finfo *fi, size_t written) +{ + int pcent = (int)((100.0 * written) / fi->size); + + pinfo = 0; + (void)fprintf(stderr, "%s => %s %zu/%zu bytes %d%% written\n", + fi->from, fi->to, written, fi->size, pcent); +} + +int +copy_file(FTSENT *entp, int dne) +{ + static char buf[MAXBSIZE]; + struct stat to_stat, *fs; + int ch, checkch, from_fd, rcount, rval, to_fd, tolnk, wcount; + char *p; + size_t ptotal = 0; + + if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) { + warn("%s", entp->fts_path); + return (1); + } + + to_fd = -1; + fs = entp->fts_statp; + tolnk = ((Rflag && !(Lflag || Hflag)) || Pflag); + + /* + * If the file exists and we're interactive, verify with the user. + * If the file DNE, set the mode to be the from file, minus setuid + * bits, modified by the umask; arguably wrong, but it makes copying + * executables work right and it's been that way forever. (The + * other choice is 666 or'ed with the execute bits on the from file + * modified by the umask.) + */ + if (!dne) { + struct stat sb; + int sval; + + if (iflag) { + (void)fprintf(stderr, "overwrite %s? ", to.p_path); + checkch = ch = getchar(); + while (ch != '\n' && ch != EOF) + ch = getchar(); + if (checkch != 'y' && checkch != 'Y') { + (void)close(from_fd); + return (0); + } + } + + sval = tolnk ? + lstat(to.p_path, &sb) : stat(to.p_path, &sb); + if (sval == -1) { + warn("stat: %s", to.p_path); + (void)close(from_fd); + return (1); + } + + if (!(tolnk && S_ISLNK(sb.st_mode))) + to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0); + } else + to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, + fs->st_mode & ~(S_ISUID | S_ISGID)); + + if (to_fd == -1 && (fflag || tolnk)) { + /* + * attempt to remove existing destination file name and + * create a new file + */ + (void)unlink(to.p_path); + to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, + fs->st_mode & ~(S_ISUID | S_ISGID)); + } + + if (to_fd == -1) { + warn("%s", to.p_path); + (void)close(from_fd); + return (1); + } + + rval = 0; + + /* if hard linking then simply close the open fds, link and return */ + if (lflag) { + (void)close(from_fd); + (void)close(to_fd); + (void)unlink(to.p_path); + if (link(entp->fts_path, to.p_path)) { + warn("%s", to.p_path); + return (1); + } + return (0); + } + /* NOTREACHED */ + + /* + * There's no reason to do anything other than close the file + * now if it's empty, so let's not bother. + */ + if (fs->st_size > 0) { + struct finfo fi; + + fi.from = entp->fts_path; + fi.to = to.p_path; + fi.size = (size_t)fs->st_size; + + /* + * Mmap and write if less than 8M (the limit is so + * we don't totally trash memory on big files). + * This is really a minor hack, but it wins some CPU back. + */ + bool use_read; + + use_read = true; + if (fs->st_size <= MMAP_MAX_SIZE) { + size_t fsize = (size_t)fs->st_size; + p = mmap(NULL, fsize, PROT_READ, MAP_FILE|MAP_SHARED, + from_fd, (off_t)0); + if (p != MAP_FAILED) { + size_t remainder; + + use_read = false; + + (void) madvise(p, (size_t)fs->st_size, + MADV_SEQUENTIAL); + + /* + * Write out the data in small chunks to + * avoid locking the output file for a + * long time if the reading the data from + * the source is slow. + */ + remainder = fsize; + do { + ssize_t chunk; + + chunk = (remainder > MMAP_MAX_WRITE) ? + MMAP_MAX_WRITE : remainder; + if (write(to_fd, &p[fsize - remainder], + chunk) != chunk) { + warn("%s", to.p_path); + rval = 1; + break; + } + remainder -= chunk; + ptotal += chunk; + if (pinfo) + progress(&fi, ptotal); + } while (remainder > 0); + + if (munmap(p, fsize) < 0) { + warn("%s", entp->fts_path); + rval = 1; + } + } + } + + if (use_read) { + while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) { + wcount = write(to_fd, buf, (size_t)rcount); + if (rcount != wcount || wcount == -1) { + warn("%s", to.p_path); + rval = 1; + break; + } + ptotal += wcount; + if (pinfo) + progress(&fi, ptotal); + } + if (rcount < 0) { + warn("%s", entp->fts_path); + rval = 1; + } + } + } + +#ifndef ANDROID + if (pflag && (fcpxattr(from_fd, to_fd) != 0)) + warn("%s: error copying extended attributes", to.p_path); +#endif + + (void)close(from_fd); + + if (rval == 1) { + (void)close(to_fd); + return (1); + } + + if (pflag && setfile(fs, to_fd)) + rval = 1; + /* + * If the source was setuid or setgid, lose the bits unless the + * copy is owned by the same user and group. + */ +#define RETAINBITS \ + (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO) + if (!pflag && dne + && fs->st_mode & (S_ISUID | S_ISGID) && fs->st_uid == myuid) { + if (fstat(to_fd, &to_stat)) { + warn("%s", to.p_path); + rval = 1; + } else if (fs->st_gid == to_stat.st_gid && + fchmod(to_fd, fs->st_mode & RETAINBITS & ~myumask)) { + warn("%s", to.p_path); + rval = 1; + } + } + if (close(to_fd)) { + warn("%s", to.p_path); + rval = 1; + } + /* set the mod/access times now after close of the fd */ + if (pflag && set_utimes(to.p_path, fs)) { + rval = 1; + } + return (rval); +} + +int +copy_link(FTSENT *p, int exists) +{ + int len; + char target[MAXPATHLEN]; + + if ((len = readlink(p->fts_path, target, sizeof(target)-1)) == -1) { + warn("readlink: %s", p->fts_path); + return (1); + } + target[len] = '\0'; + if (exists && unlink(to.p_path)) { + warn("unlink: %s", to.p_path); + return (1); + } + if (symlink(target, to.p_path)) { + warn("symlink: %s", target); + return (1); + } + return (pflag ? setfile(p->fts_statp, 0) : 0); +} + +int +copy_fifo(struct stat *from_stat, int exists) +{ + if (exists && unlink(to.p_path)) { + warn("unlink: %s", to.p_path); + return (1); + } + if (mkfifo(to.p_path, from_stat->st_mode)) { + warn("mkfifo: %s", to.p_path); + return (1); + } + return (pflag ? setfile(from_stat, 0) : 0); +} + +int +copy_special(struct stat *from_stat, int exists) +{ + if (exists && unlink(to.p_path)) { + warn("unlink: %s", to.p_path); + return (1); + } + if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) { + warn("mknod: %s", to.p_path); + return (1); + } + return (pflag ? setfile(from_stat, 0) : 0); +} + + +/* + * Function: setfile + * + * Purpose: + * Set the owner/group/permissions for the "to" file to the information + * in the stat structure. If fd is zero, also call set_utimes() to set + * the mod/access times. If fd is non-zero, the caller must do a utimes + * itself after close(fd). + */ +int +setfile(struct stat *fs, int fd) +{ + int rval, islink; + + rval = 0; + islink = S_ISLNK(fs->st_mode); + fs->st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO; + + /* + * Changing the ownership probably won't succeed, unless we're root + * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting + * the mode; current BSD behavior is to remove all setuid bits on + * chown. If chown fails, lose setuid/setgid bits. + */ + if (fd ? fchown(fd, fs->st_uid, fs->st_gid) : + lchown(to.p_path, fs->st_uid, fs->st_gid)) { + if (errno != EPERM) { + warn("chown: %s", to.p_path); + rval = 1; + } + fs->st_mode &= ~(S_ISUID | S_ISGID); + } +#ifdef ANDROID + if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) { +#else + if (fd ? fchmod(fd, fs->st_mode) : lchmod(to.p_path, fs->st_mode)) { +#endif + warn("chmod: %s", to.p_path); + rval = 1; + } + +#ifndef ANDROID + if (!islink && !Nflag) { + unsigned long fflags = fs->st_flags; + /* + * XXX + * NFS doesn't support chflags; ignore errors unless + * there's reason to believe we're losing bits. + * (Note, this still won't be right if the server + * supports flags and we were trying to *remove* flags + * on a file that we copied, i.e., that we didn't create.) + */ + errno = 0; + if ((fd ? fchflags(fd, fflags) : + chflags(to.p_path, fflags)) == -1) + if (errno != EOPNOTSUPP || fs->st_flags != 0) { + warn("chflags: %s", to.p_path); + rval = 1; + } + } +#endif + /* if fd is non-zero, caller must call set_utimes() after close() */ + if (fd == 0 && set_utimes(to.p_path, fs)) + rval = 1; + return (rval); +} + +void +cp_usage(void) +{ + (void)fprintf(stderr, + "usage: cp [-R [-H | -L | -P]] [-f | -i] [-alNpv] src target\n" + " cp [-R [-H | -L | -P]] [-f | -i] [-alNpv] src1 ... srcN directory\n"); + exit(1); + /* NOTREACHED */ +} diff --git a/toolbox/dd.c b/toolbox/dd.c index c6af3ea..350f1d2 100644 --- a/toolbox/dd.c +++ b/toolbox/dd.c @@ -64,7 +64,7 @@ __RCSID("$NetBSD: dd.c,v 1.37 2004/01/17 21:00:16 dbj Exp $"); #include "dd.h" -#define NO_CONV +//#define NO_CONV //#include "extern.h" void block(void); @@ -91,13 +91,11 @@ extern u_int ddflags; extern u_int files_cnt; extern int progress; extern const u_char *ctab; -extern const u_char a2e_32V[], a2e_POSIX[]; -extern const u_char e2a_32V[], e2a_POSIX[]; -extern const u_char a2ibm_32V[], a2ibm_POSIX[]; -extern u_char casetab[]; +#define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define DEFFILEMODE (S_IRUSR | S_IWUSR) static void dd_close(void); static void dd_in(void); @@ -188,14 +186,14 @@ setup(void) } else { #define OFLAGS \ (O_CREAT | (ddflags & (C_SEEK | C_NOTRUNC) ? 0 : O_TRUNC)) - out.fd = open(out.name, O_RDWR | OFLAGS /*, DEFFILEMODE */); + out.fd = open(out.name, O_RDWR | OFLAGS, DEFFILEMODE); /* * May not have read access, so try again with write only. * Without read we may have a problem if output also does * not support seeks. */ if (out.fd < 0) { - out.fd = open(out.name, O_WRONLY | OFLAGS /*, DEFFILEMODE */); + out.fd = open(out.name, O_WRONLY | OFLAGS, DEFFILEMODE); out.flags |= NOREAD; } if (out.fd < 0) { @@ -243,42 +241,6 @@ setup(void) if ((ddflags & (C_OF | C_SEEK | C_NOTRUNC)) == (C_OF | C_SEEK)) (void)ftruncate(out.fd, (off_t)out.offset * out.dbsz); - /* - * If converting case at the same time as another conversion, build a - * table that does both at once. If just converting case, use the - * built-in tables. - */ - if (ddflags & (C_LCASE|C_UCASE)) { -#ifdef NO_CONV - /* Should not get here, but just in case... */ - fprintf(stderr, "case conv and -DNO_CONV\n"); - exit(1); - /* NOTREACHED */ -#else /* NO_CONV */ - u_int cnt; - - if (ddflags & C_ASCII || ddflags & C_EBCDIC) { - if (ddflags & C_LCASE) { - for (cnt = 0; cnt < 0377; ++cnt) - casetab[cnt] = tolower(ctab[cnt]); - } else { - for (cnt = 0; cnt < 0377; ++cnt) - casetab[cnt] = toupper(ctab[cnt]); - } - } else { - if (ddflags & C_LCASE) { - for (cnt = 0; cnt < 0377; ++cnt) - casetab[cnt] = tolower(cnt); - } else { - for (cnt = 0; cnt < 0377; ++cnt) - casetab[cnt] = toupper(cnt); - } - } - - ctab = casetab; -#endif /* NO_CONV */ - } - (void)gettimeofday(&st.start, NULL); /* Statistics timestamp. */ } @@ -796,6 +758,9 @@ def(void) void def_close(void) { + if (ddflags & C_FDATASYNC) { + fdatasync(out.fd); + } /* Just update the count, everything is already in the buffer. */ if (in.dbcnt) @@ -1301,21 +1266,14 @@ static const struct conv { u_int set, noset; const u_char *ctab; } clist[] = { - { "ascii", C_ASCII, C_EBCDIC, e2a_POSIX }, { "block", C_BLOCK, C_UNBLOCK, NULL }, - { "ebcdic", C_EBCDIC, C_ASCII, a2e_POSIX }, - { "ibm", C_EBCDIC, C_ASCII, a2ibm_POSIX }, - { "lcase", C_LCASE, C_UCASE, NULL }, + { "fdatasync", C_FDATASYNC, 0, NULL }, { "noerror", C_NOERROR, 0, NULL }, { "notrunc", C_NOTRUNC, 0, NULL }, - { "oldascii", C_ASCII, C_EBCDIC, e2a_32V }, - { "oldebcdic", C_EBCDIC, C_ASCII, a2e_32V }, - { "oldibm", C_EBCDIC, C_ASCII, a2ibm_32V }, { "osync", C_OSYNC, C_BS, NULL }, { "sparse", C_SPARSE, 0, NULL }, { "swab", C_SWAB, 0, NULL }, { "sync", C_SYNC, 0, NULL }, - { "ucase", C_UCASE, C_LCASE, NULL }, { "unblock", C_UNBLOCK, C_BLOCK, NULL }, /* If you add items to this table, be sure to add the * conversions to the C_BS check in the jcl routine above. diff --git a/toolbox/dd.h b/toolbox/dd.h index cca1024..89f2833 100644 --- a/toolbox/dd.h +++ b/toolbox/dd.h @@ -91,3 +91,4 @@ typedef struct { #define C_UNBLOCK 0x80000 #define C_OSYNC 0x100000 #define C_SPARSE 0x200000 +#define C_FDATASYNC 0x400000 diff --git a/toolbox/df.c b/toolbox/df.c index 63940a1..9cd0743 100644 --- a/toolbox/df.c +++ b/toolbox/df.c @@ -9,16 +9,22 @@ static int ok = EXIT_SUCCESS; static void printsize(long long n) { char unit = 'K'; - n /= 1024; - if (n > 1024) { + long long t; + + n *= 10; + + if (n > 1024*1024*10) { n /= 1024; unit = 'M'; } - if (n > 1024) { + + if (n > 1024*1024*10) { n /= 1024; unit = 'G'; } - printf("%4lld%c", n, unit); + + t = (n + 512) / 1024; + printf("%4lld.%1lld%c", t/10, t%10, unit); } static void df(char *s, int always) { @@ -41,7 +47,7 @@ static void df(char *s, int always) { } int df_main(int argc, char *argv[]) { - printf("Filesystem Size Used Free Blksize\n"); + printf("Filesystem Size Used Free Blksize\n"); if (argc == 1) { char s[2000]; FILE *f = fopen("/proc/mounts", "r"); diff --git a/toolbox/du.c b/toolbox/du.c new file mode 100644 index 0000000..06374a4 --- /dev/null +++ b/toolbox/du.c @@ -0,0 +1,322 @@ +/* $NetBSD: du.c,v 1.33 2008/07/30 22:03:40 dsl Exp $ */ + +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Newcomb. + * + * 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 University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1994\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)du.c 8.5 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: du.c,v 1.33 2008/07/30 22:03:40 dsl Exp $"); +#endif +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <dirent.h> +#include <err.h> +#include <errno.h> +#include <fts.h> +#include <util.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <limits.h> + +int linkchk(dev_t, ino_t); +void prstat(const char *, int64_t); +void usage(void); + +long blocksize; + +#define howmany(x, y) (((x)+((y)-1))/(y)) + +int +du_main(int argc, char *argv[]) +{ + FTS *fts; + FTSENT *p; + int64_t totalblocks; + int ftsoptions, listfiles; + int depth; + int Hflag, Lflag, aflag, ch, cflag, dflag, gkmflag, nflag, rval, sflag; + const char *noargv[2]; + + Hflag = Lflag = aflag = cflag = dflag = gkmflag = sflag = 0; + totalblocks = 0; + ftsoptions = FTS_PHYSICAL; + depth = INT_MAX; + while ((ch = getopt(argc, argv, "HLPacd:ghkmnrsx")) != -1) + switch (ch) { + case 'H': + Hflag = 1; + Lflag = 0; + break; + case 'L': + Lflag = 1; + Hflag = 0; + break; + case 'P': + Hflag = Lflag = 0; + break; + case 'a': + aflag = 1; + break; + case 'c': + cflag = 1; + break; + case 'd': + dflag = 1; + depth = atoi(optarg); + if (depth < 0 || depth > SHRT_MAX) { + warnx("invalid argument to option d: %s", + optarg); + usage(); + } + break; + case 'g': + blocksize = 1024 * 1024 * 1024; + gkmflag = 1; + break; + case 'k': + blocksize = 1024; + gkmflag = 1; + break; + case 'm': + blocksize = 1024 * 1024; + gkmflag = 1; + break; + case 'r': + break; + case 's': + sflag = 1; + break; + case 'x': + ftsoptions |= FTS_XDEV; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + /* + * XXX + * Because of the way that fts(3) works, logical walks will not count + * the blocks actually used by symbolic links. We rationalize this by + * noting that users computing logical sizes are likely to do logical + * copies, so not counting the links is correct. The real reason is + * that we'd have to re-implement the kernel's symbolic link traversing + * algorithm to get this right. If, for example, you have relative + * symbolic links referencing other relative symbolic links, it gets + * very nasty, very fast. The bottom line is that it's documented in + * the man page, so it's a feature. + */ + if (Hflag) + ftsoptions |= FTS_COMFOLLOW; + if (Lflag) { + ftsoptions &= ~FTS_PHYSICAL; + ftsoptions |= FTS_LOGICAL; + } + + listfiles = 0; + if (aflag) { + if (sflag || dflag) + usage(); + listfiles = 1; + } else if (sflag) { + if (dflag) + usage(); + depth = 0; + } + + if (!*argv) { + noargv[0] = "."; + noargv[1] = NULL; + argv = __UNCONST(noargv); + } + + if (!gkmflag) + blocksize = 512; + blocksize /= 512; + + if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL) + err(1, "fts_open `%s'", *argv); + + for (rval = 0; (p = fts_read(fts)) != NULL;) { + switch (p->fts_info) { + case FTS_D: /* Ignore. */ + break; + case FTS_DP: + p->fts_parent->fts_number += + p->fts_number += p->fts_statp->st_blocks; + if (cflag) + totalblocks += p->fts_statp->st_blocks; + /* + * If listing each directory, or not listing files + * or directories and this is post-order of the + * root of a traversal, display the total. + */ + if (p->fts_level <= depth + || (!listfiles && !p->fts_level)) + prstat(p->fts_path, p->fts_number); + break; + case FTS_DC: /* Ignore. */ + break; + case FTS_DNR: /* Warn, continue. */ + case FTS_ERR: + case FTS_NS: + warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); + rval = 1; + break; + default: + if (p->fts_statp->st_nlink > 1 && + linkchk(p->fts_statp->st_dev, p->fts_statp->st_ino)) + break; + /* + * If listing each file, or a non-directory file was + * the root of a traversal, display the total. + */ + if (listfiles || !p->fts_level) + prstat(p->fts_path, p->fts_statp->st_blocks); + p->fts_parent->fts_number += p->fts_statp->st_blocks; + if (cflag) + totalblocks += p->fts_statp->st_blocks; + } + } + if (errno) + err(1, "fts_read"); + if (cflag) + prstat("total", totalblocks); + exit(rval); +} + +void +prstat(const char *fname, int64_t blocks) +{ + (void)printf("%lld\t%s\n", + (long long)howmany(blocks, (int64_t)blocksize), + fname); +} + +int +linkchk(dev_t dev, ino_t ino) +{ + static struct entry { + dev_t dev; + ino_t ino; + } *htable; + static int htshift; /* log(allocated size) */ + static int htmask; /* allocated size - 1 */ + static int htused; /* 2*number of insertions */ + static int sawzero; /* Whether zero is in table or not */ + int h, h2; + uint64_t tmp; + /* this constant is (1<<64)/((1+sqrt(5))/2) + * aka (word size)/(golden ratio) + */ + const uint64_t HTCONST = 11400714819323198485ULL; + const int HTBITS = CHAR_BIT * sizeof(tmp); + + /* Never store zero in hashtable */ + if (dev == 0 && ino == 0) { + h = sawzero; + sawzero = 1; + return h; + } + + /* Extend hash table if necessary, keep load under 0.5 */ + if (htused<<1 >= htmask) { + struct entry *ohtable; + + if (!htable) + htshift = 10; /* starting hashtable size */ + else + htshift++; /* exponential hashtable growth */ + + htmask = (1 << htshift) - 1; + htused = 0; + + ohtable = htable; + htable = calloc(htmask+1, sizeof(*htable)); + if (!htable) + err(1, "calloc"); + + /* populate newly allocated hashtable */ + if (ohtable) { + int i; + for (i = 0; i <= htmask>>1; i++) + if (ohtable[i].ino || ohtable[i].dev) + linkchk(ohtable[i].dev, ohtable[i].ino); + free(ohtable); + } + } + + /* multiplicative hashing */ + tmp = dev; + tmp <<= HTBITS>>1; + tmp |= ino; + tmp *= HTCONST; + h = tmp >> (HTBITS - htshift); + h2 = 1 | ( tmp >> (HTBITS - (htshift<<1) - 1)); /* must be odd */ + + /* open address hashtable search with double hash probing */ + while (htable[h].ino || htable[h].dev) { + if ((htable[h].ino == ino) && (htable[h].dev == dev)) + return 1; + h = (h + h2) & htmask; + } + + /* Insert the current entry into hashtable */ + htable[h].dev = dev; + htable[h].ino = ino; + htused++; + return 0; +} + +void +usage(void) +{ + + (void)fprintf(stderr, + "usage: du [-H | -L | -P] [-a | -d depth | -s] [-cgkmrx] [file ...]\n"); + exit(1); +} diff --git a/toolbox/grep/fastgrep.c b/toolbox/grep/fastgrep.c new file mode 100644 index 0000000..2fcd864 --- /dev/null +++ b/toolbox/grep/fastgrep.c @@ -0,0 +1,336 @@ +/* $OpenBSD: util.c,v 1.36 2007/10/02 17:59:18 otto Exp $ */ +/* $FreeBSD: head/usr.bin/grep/fastgrep.c 211496 2010-08-19 09:28:59Z des $ */ + +/*- + * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav + * Copyright (C) 2008 Gabor Kovesdan <gabor@FreeBSD.org> + * All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * XXX: This file is a speed up for grep to cover the defects of the + * regex library. These optimizations should practically be implemented + * there keeping this code clean. This is a future TODO, but for the + * meantime, we need to use this workaround. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +__RCSID("$NetBSD: fastgrep.c,v 1.5 2011/04/18 03:27:40 joerg Exp $"); + +#include <limits.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> +#include <wctype.h> + +#include "grep.h" + +static inline int grep_cmp(const unsigned char *, const unsigned char *, size_t); +static inline void grep_revstr(unsigned char *, int); + +void +fgrepcomp(fastgrep_t *fg, const char *pat) +{ + unsigned int i; + + /* Initialize. */ + fg->len = strlen(pat); + fg->bol = false; + fg->eol = false; + fg->reversed = false; + + fg->pattern = (unsigned char *)grep_strdup(pat); + + /* Preprocess pattern. */ + for (i = 0; i <= UCHAR_MAX; i++) + fg->qsBc[i] = fg->len; + for (i = 1; i < fg->len; i++) + fg->qsBc[fg->pattern[i]] = fg->len - i; +} + +/* + * Returns: -1 on failure, 0 on success + */ +int +fastcomp(fastgrep_t *fg, const char *pat) +{ + unsigned int i; + int firstHalfDot = -1; + int firstLastHalfDot = -1; + int hasDot = 0; + int lastHalfDot = 0; + int shiftPatternLen; + + /* Initialize. */ + fg->len = strlen(pat); + fg->bol = false; + fg->eol = false; + fg->reversed = false; + fg->word = wflag; + + /* Remove end-of-line character ('$'). */ + if (fg->len > 0 && pat[fg->len - 1] == '$') { + fg->eol = true; + fg->len--; + } + + /* Remove beginning-of-line character ('^'). */ + if (pat[0] == '^') { + fg->bol = true; + fg->len--; + pat++; + } + + if (fg->len >= 14 && + memcmp(pat, "[[:<:]]", 7) == 0 && + memcmp(pat + fg->len - 7, "[[:>:]]", 7) == 0) { + fg->len -= 14; + pat += 7; + /* Word boundary is handled separately in util.c */ + fg->word = true; + } + + /* + * pat has been adjusted earlier to not include '^', '$' or + * the word match character classes at the beginning and ending + * of the string respectively. + */ + fg->pattern = grep_malloc(fg->len + 1); + memcpy(fg->pattern, pat, fg->len); + fg->pattern[fg->len] = '\0'; + + /* Look for ways to cheat...er...avoid the full regex engine. */ + for (i = 0; i < fg->len; i++) { + /* Can still cheat? */ + if (fg->pattern[i] == '.') { + hasDot = i; + if (i < fg->len / 2) { + if (firstHalfDot < 0) + /* Closest dot to the beginning */ + firstHalfDot = i; + } else { + /* Closest dot to the end of the pattern. */ + lastHalfDot = i; + if (firstLastHalfDot < 0) + firstLastHalfDot = i; + } + } else { + /* Free memory and let others know this is empty. */ + free(fg->pattern); + fg->pattern = NULL; + return (-1); + } + } + + /* + * Determine if a reverse search would be faster based on the placement + * of the dots. + */ + if ((!(lflag || cflag)) && ((!(fg->bol || fg->eol)) && + ((lastHalfDot) && ((firstHalfDot < 0) || + ((fg->len - (lastHalfDot + 1)) < (size_t)firstHalfDot)))) && + !oflag && !color) { + fg->reversed = true; + hasDot = fg->len - (firstHalfDot < 0 ? + firstLastHalfDot : firstHalfDot) - 1; + grep_revstr(fg->pattern, fg->len); + } + + /* + * Normal Quick Search would require a shift based on the position the + * next character after the comparison is within the pattern. With + * wildcards, the position of the last dot effects the maximum shift + * distance. + * The closer to the end the wild card is the slower the search. A + * reverse version of this algorithm would be useful for wildcards near + * the end of the string. + * + * Examples: + * Pattern Max shift + * ------- --------- + * this 5 + * .his 4 + * t.is 3 + * th.s 2 + * thi. 1 + */ + + /* Adjust the shift based on location of the last dot ('.'). */ + shiftPatternLen = fg->len - hasDot; + + /* Preprocess pattern. */ + for (i = 0; i <= (signed)UCHAR_MAX; i++) + fg->qsBc[i] = shiftPatternLen; + for (i = hasDot + 1; i < fg->len; i++) { + fg->qsBc[fg->pattern[i]] = fg->len - i; + } + + /* + * Put pattern back to normal after pre-processing to allow for easy + * comparisons later. + */ + if (fg->reversed) + grep_revstr(fg->pattern, fg->len); + + return (0); +} + +int +grep_search(fastgrep_t *fg, const unsigned char *data, size_t len, regmatch_t *pmatch) +{ + unsigned int j; + int ret = REG_NOMATCH; + + if (pmatch->rm_so == (ssize_t)len) + return (ret); + + if (fg->bol && pmatch->rm_so != 0) { + pmatch->rm_so = len; + pmatch->rm_eo = len; + return (ret); + } + + /* No point in going farther if we do not have enough data. */ + if (len < fg->len) + return (ret); + + /* Only try once at the beginning or ending of the line. */ + if (fg->bol || fg->eol) { + /* Simple text comparison. */ + /* Verify data is >= pattern length before searching on it. */ + if (len >= fg->len) { + /* Determine where in data to start search at. */ + j = fg->eol ? len - fg->len : 0; + if (!((fg->bol && fg->eol) && (len != fg->len))) + if (grep_cmp(fg->pattern, data + j, + fg->len) == -1) { + pmatch->rm_so = j; + pmatch->rm_eo = j + fg->len; + ret = 0; + } + } + } else if (fg->reversed) { + /* Quick Search algorithm. */ + j = len; + do { + if (grep_cmp(fg->pattern, data + j - fg->len, + fg->len) == -1) { + pmatch->rm_so = j - fg->len; + pmatch->rm_eo = j; + ret = 0; + break; + } + /* Shift if within bounds, otherwise, we are done. */ + if (j == fg->len) + break; + j -= fg->qsBc[data[j - fg->len - 1]]; + } while (j >= fg->len); + } else { + /* Quick Search algorithm. */ + j = pmatch->rm_so; + do { + if (grep_cmp(fg->pattern, data + j, fg->len) == -1) { + pmatch->rm_so = j; + pmatch->rm_eo = j + fg->len; + ret = 0; + break; + } + + /* Shift if within bounds, otherwise, we are done. */ + if (j + fg->len == len) + break; + else + j += fg->qsBc[data[j + fg->len]]; + } while (j <= (len - fg->len)); + } + + return (ret); +} + +/* + * Returns: i >= 0 on failure (position that it failed) + * -1 on success + */ +static inline int +grep_cmp(const unsigned char *pat, const unsigned char *data, size_t len) +{ + size_t size; + wchar_t *wdata, *wpat; + unsigned int i; + + if (iflag) { + if ((size = mbstowcs(NULL, (const char *)data, 0)) == + ((size_t) - 1)) + return (-1); + + wdata = grep_malloc(size * sizeof(wint_t)); + + if (mbstowcs(wdata, (const char *)data, size) == + ((size_t) - 1)) + return (-1); + + if ((size = mbstowcs(NULL, (const char *)pat, 0)) == + ((size_t) - 1)) + return (-1); + + wpat = grep_malloc(size * sizeof(wint_t)); + + if (mbstowcs(wpat, (const char *)pat, size) == ((size_t) - 1)) + return (-1); + for (i = 0; i < len; i++) { + if ((towlower(wpat[i]) == towlower(wdata[i])) || + ((grepbehave != GREP_FIXED) && wpat[i] == L'.')) + continue; + free(wpat); + free(wdata); + return (i); + } + } else { + for (i = 0; i < len; i++) { + if ((pat[i] == data[i]) || ((grepbehave != GREP_FIXED) && + pat[i] == '.')) + continue; + return (i); + } + } + return (-1); +} + +static inline void +grep_revstr(unsigned char *str, int len) +{ + int i; + char c; + + for (i = 0; i < len / 2; i++) { + c = str[i]; + str[i] = str[len - i - 1]; + str[len - i - 1] = c; + } +} diff --git a/toolbox/grep/file.c b/toolbox/grep/file.c new file mode 100644 index 0000000..86b7658 --- /dev/null +++ b/toolbox/grep/file.c @@ -0,0 +1,269 @@ +/* $NetBSD: file.c,v 1.7 2011/04/18 22:46:48 joerg Exp $ */ +/* $FreeBSD: head/usr.bin/grep/file.c 211496 2010-08-19 09:28:59Z des $ */ +/* $OpenBSD: file.c,v 1.11 2010/07/02 20:48:48 nicm Exp $ */ + +/*- + * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav + * Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org> + * Copyright (C) 2010 Dimitry Andric <dimitry@andric.com> + * All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +__RCSID("$NetBSD: file.c,v 1.7 2011/04/18 22:46:48 joerg Exp $"); + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/stat.h> + +#ifndef ANDROID +#include <bzlib.h> +#endif +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <wchar.h> +#include <wctype.h> +#ifndef ANDROID +#include <zlib.h> +#endif + +#include "grep.h" + +#define MAXBUFSIZ (32 * 1024) +#define LNBUFBUMP 80 + +#ifndef ANDROID +static gzFile gzbufdesc; +static BZFILE* bzbufdesc; +#endif + +static unsigned char buffer[MAXBUFSIZ]; +static unsigned char *bufpos; +static size_t bufrem; + +static unsigned char *lnbuf; +static size_t lnbuflen; + +static inline int +grep_refill(struct file *f) +{ + ssize_t nr; + int bzerr; + + bufpos = buffer; + bufrem = 0; + +#ifndef ANDROID + if (filebehave == FILE_GZIP) + nr = gzread(gzbufdesc, buffer, MAXBUFSIZ); + else if (filebehave == FILE_BZIP && bzbufdesc != NULL) { + nr = BZ2_bzRead(&bzerr, bzbufdesc, buffer, MAXBUFSIZ); + switch (bzerr) { + case BZ_OK: + case BZ_STREAM_END: + /* No problem, nr will be okay */ + break; + case BZ_DATA_ERROR_MAGIC: + /* + * As opposed to gzread(), which simply returns the + * plain file data, if it is not in the correct + * compressed format, BZ2_bzRead() instead aborts. + * + * So, just restart at the beginning of the file again, + * and use plain reads from now on. + */ + BZ2_bzReadClose(&bzerr, bzbufdesc); + bzbufdesc = NULL; + if (lseek(f->fd, 0, SEEK_SET) == -1) + return (-1); + nr = read(f->fd, buffer, MAXBUFSIZ); + break; + default: + /* Make sure we exit with an error */ + nr = -1; + } + } else +#endif + nr = read(f->fd, buffer, MAXBUFSIZ); + + if (nr < 0) + return (-1); + + bufrem = nr; + return (0); +} + +static inline int +grep_lnbufgrow(size_t newlen) +{ + + if (lnbuflen < newlen) { + lnbuf = grep_realloc(lnbuf, newlen); + lnbuflen = newlen; + } + + return (0); +} + +char * +grep_fgetln(struct file *f, size_t *lenp) +{ + unsigned char *p; + char *ret; + size_t len; + size_t off; + ptrdiff_t diff; + + /* Fill the buffer, if necessary */ + if (bufrem == 0 && grep_refill(f) != 0) + goto error; + + if (bufrem == 0) { + /* Return zero length to indicate EOF */ + *lenp = 0; + return ((char *)bufpos); + } + + /* Look for a newline in the remaining part of the buffer */ + if ((p = memchr(bufpos, line_sep, bufrem)) != NULL) { + ++p; /* advance over newline */ + ret = (char *)bufpos; + len = p - bufpos; + bufrem -= len; + bufpos = p; + *lenp = len; + return (ret); + } + + /* We have to copy the current buffered data to the line buffer */ + for (len = bufrem, off = 0; ; len += bufrem) { + /* Make sure there is room for more data */ + if (grep_lnbufgrow(len + LNBUFBUMP)) + goto error; + memcpy(lnbuf + off, bufpos, len - off); + off = len; + if (grep_refill(f) != 0) + goto error; + if (bufrem == 0) + /* EOF: return partial line */ + break; + if ((p = memchr(bufpos, line_sep, bufrem)) == NULL) + continue; + /* got it: finish up the line (like code above) */ + ++p; + diff = p - bufpos; + len += diff; + if (grep_lnbufgrow(len)) + goto error; + memcpy(lnbuf + off, bufpos, diff); + bufrem -= diff; + bufpos = p; + break; + } + *lenp = len; + return ((char *)lnbuf); + +error: + *lenp = 0; + return (NULL); +} + +static inline struct file * +grep_file_init(struct file *f) +{ + +#ifndef ANDROID + if (filebehave == FILE_GZIP && + (gzbufdesc = gzdopen(f->fd, "r")) == NULL) + goto error; + + if (filebehave == FILE_BZIP && + (bzbufdesc = BZ2_bzdopen(f->fd, "r")) == NULL) + goto error; +#endif + + /* Fill read buffer, also catches errors early */ + if (grep_refill(f) != 0) + goto error; + + /* Check for binary stuff, if necessary */ + if (!nulldataflag && binbehave != BINFILE_TEXT && + memchr(bufpos, '\0', bufrem) != NULL) + f->binary = true; + + return (f); +error: + close(f->fd); + free(f); + return (NULL); +} + +/* + * Opens a file for processing. + */ +struct file * +grep_open(const char *path) +{ + struct file *f; + + f = grep_malloc(sizeof *f); + memset(f, 0, sizeof *f); + if (path == NULL) { + /* Processing stdin implies --line-buffered. */ + lbflag = true; + f->fd = STDIN_FILENO; + } else if ((f->fd = open(path, O_RDONLY)) == -1) { + free(f); + return (NULL); + } + + return (grep_file_init(f)); +} + +/* + * Closes a file. + */ +void +grep_close(struct file *f) +{ + + close(f->fd); + + /* Reset read buffer and line buffer */ + bufpos = buffer; + bufrem = 0; + + free(lnbuf); + lnbuf = NULL; + lnbuflen = 0; +} diff --git a/toolbox/grep/grep.c b/toolbox/grep/grep.c new file mode 100644 index 0000000..b5bb2ef --- /dev/null +++ b/toolbox/grep/grep.c @@ -0,0 +1,710 @@ +/* $NetBSD: grep.c,v 1.11 2012/05/06 22:27:00 joerg Exp $ */ +/* $FreeBSD: head/usr.bin/grep/grep.c 211519 2010-08-19 22:55:17Z delphij $ */ +/* $OpenBSD: grep.c,v 1.42 2010/07/02 22:18:03 tedu Exp $ */ + +/*- + * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav + * Copyright (C) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org> + * All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +__RCSID("$NetBSD: grep.c,v 1.11 2012/05/06 22:27:00 joerg Exp $"); + +#include <sys/stat.h> +#include <sys/types.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <getopt.h> +#include <limits.h> +#include <libgen.h> +#include <locale.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "grep.h" + +#ifndef WITHOUT_NLS +#include <nl_types.h> +nl_catd catalog; +#endif + +/* + * Default messags to use when NLS is disabled or no catalogue + * is found. + */ +const char *errstr[] = { + "", +/* 1*/ "(standard input)", +/* 2*/ "cannot read bzip2 compressed file", +/* 3*/ "unknown %s option", +/* 4*/ "usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZz] [-A num] [-B num] [-C[num]]\n", +/* 5*/ "\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n", +/* 6*/ "\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n", +/* 7*/ "\t[pattern] [file ...]\n", +/* 8*/ "Binary file %s matches\n", +/* 9*/ "%s (BSD grep) %s\n", +}; + +/* Flags passed to regcomp() and regexec() */ +int cflags = 0; +int eflags = REG_STARTEND; + +/* Searching patterns */ +unsigned int patterns, pattern_sz; +char **pattern; +regex_t *r_pattern; +fastgrep_t *fg_pattern; + +/* Filename exclusion/inclusion patterns */ +unsigned int fpatterns, fpattern_sz; +unsigned int dpatterns, dpattern_sz; +struct epat *dpattern, *fpattern; + +/* For regex errors */ +char re_error[RE_ERROR_BUF + 1]; + +/* Command-line flags */ +unsigned long long Aflag; /* -A x: print x lines trailing each match */ +unsigned long long Bflag; /* -B x: print x lines leading each match */ +bool Hflag; /* -H: always print file name */ +bool Lflag; /* -L: only show names of files with no matches */ +bool bflag; /* -b: show block numbers for each match */ +bool cflag; /* -c: only show a count of matching lines */ +bool hflag; /* -h: don't print filename headers */ +bool iflag; /* -i: ignore case */ +bool lflag; /* -l: only show names of files with matches */ +bool mflag; /* -m x: stop reading the files after x matches */ +unsigned long long mcount; /* count for -m */ +bool nflag; /* -n: show line numbers in front of matching lines */ +bool oflag; /* -o: print only matching part */ +bool qflag; /* -q: quiet mode (don't output anything) */ +bool sflag; /* -s: silent mode (ignore errors) */ +bool vflag; /* -v: only show non-matching lines */ +bool wflag; /* -w: pattern must start and end on word boundaries */ +bool xflag; /* -x: pattern must match entire line */ +bool lbflag; /* --line-buffered */ +bool nullflag; /* --null */ +bool nulldataflag; /* --null-data */ +unsigned char line_sep = '\n'; /* 0 for --null-data */ +char *label; /* --label */ +const char *color; /* --color */ +int grepbehave = GREP_BASIC; /* -EFGP: type of the regex */ +int binbehave = BINFILE_BIN; /* -aIU: handling of binary files */ +int filebehave = FILE_STDIO; /* -JZ: normal, gzip or bzip2 file */ +int devbehave = DEV_READ; /* -D: handling of devices */ +int dirbehave = DIR_READ; /* -dRr: handling of directories */ +int linkbehave = LINK_READ; /* -OpS: handling of symlinks */ + +bool dexclude, dinclude; /* --exclude-dir and --include-dir */ +bool fexclude, finclude; /* --exclude and --include */ + +enum { + BIN_OPT = CHAR_MAX + 1, + COLOR_OPT, + DECOMPRESS_OPT, + HELP_OPT, + MMAP_OPT, + LINEBUF_OPT, + LABEL_OPT, + R_EXCLUDE_OPT, + R_INCLUDE_OPT, + R_DEXCLUDE_OPT, + R_DINCLUDE_OPT +}; + +static inline const char *init_color(const char *); + +/* Housekeeping */ +int tail; /* lines left to print */ +bool notfound; /* file not found */ + +extern char *__progname; + +/* + * Prints usage information and returns 2. + */ +__dead static void +usage(void) +{ + fprintf(stderr, getstr(4), __progname); + fprintf(stderr, "%s", getstr(5)); + fprintf(stderr, "%s", getstr(5)); + fprintf(stderr, "%s", getstr(6)); + fprintf(stderr, "%s", getstr(7)); + exit(2); +} + +static const char optstr[] = + "0123456789A:B:C:D:EFGHIJLOPSRUVZabcd:e:f:hilm:nopqrsuvwxyz"; + +struct option long_options[] = +{ + {"binary-files", required_argument, NULL, BIN_OPT}, + {"decompress", no_argument, NULL, DECOMPRESS_OPT}, + {"help", no_argument, NULL, HELP_OPT}, + {"mmap", no_argument, NULL, MMAP_OPT}, + {"line-buffered", no_argument, NULL, LINEBUF_OPT}, + {"label", required_argument, NULL, LABEL_OPT}, + {"color", optional_argument, NULL, COLOR_OPT}, + {"colour", optional_argument, NULL, COLOR_OPT}, + {"exclude", required_argument, NULL, R_EXCLUDE_OPT}, + {"include", required_argument, NULL, R_INCLUDE_OPT}, + {"exclude-dir", required_argument, NULL, R_DEXCLUDE_OPT}, + {"include-dir", required_argument, NULL, R_DINCLUDE_OPT}, + {"after-context", required_argument, NULL, 'A'}, + {"text", no_argument, NULL, 'a'}, + {"before-context", required_argument, NULL, 'B'}, + {"byte-offset", no_argument, NULL, 'b'}, + {"context", optional_argument, NULL, 'C'}, + {"count", no_argument, NULL, 'c'}, + {"devices", required_argument, NULL, 'D'}, + {"directories", required_argument, NULL, 'd'}, + {"extended-regexp", no_argument, NULL, 'E'}, + {"regexp", required_argument, NULL, 'e'}, + {"fixed-strings", no_argument, NULL, 'F'}, + {"file", required_argument, NULL, 'f'}, + {"basic-regexp", no_argument, NULL, 'G'}, + {"no-filename", no_argument, NULL, 'h'}, + {"with-filename", no_argument, NULL, 'H'}, + {"ignore-case", no_argument, NULL, 'i'}, + {"bz2decompress", no_argument, NULL, 'J'}, + {"files-with-matches", no_argument, NULL, 'l'}, + {"files-without-match", no_argument, NULL, 'L'}, + {"max-count", required_argument, NULL, 'm'}, + {"line-number", no_argument, NULL, 'n'}, + {"only-matching", no_argument, NULL, 'o'}, + {"quiet", no_argument, NULL, 'q'}, + {"silent", no_argument, NULL, 'q'}, + {"recursive", no_argument, NULL, 'r'}, + {"no-messages", no_argument, NULL, 's'}, + {"binary", no_argument, NULL, 'U'}, + {"unix-byte-offsets", no_argument, NULL, 'u'}, + {"invert-match", no_argument, NULL, 'v'}, + {"version", no_argument, NULL, 'V'}, + {"word-regexp", no_argument, NULL, 'w'}, + {"line-regexp", no_argument, NULL, 'x'}, + {"null", no_argument, NULL, 'Z'}, + {"null-data", no_argument, NULL, 'z'}, + {NULL, no_argument, NULL, 0} +}; + +/* + * Adds a searching pattern to the internal array. + */ +static void +add_pattern(char *pat, size_t len) +{ + + /* TODO: Check for empty patterns and shortcut */ + + /* Increase size if necessary */ + if (patterns == pattern_sz) { + pattern_sz *= 2; + pattern = grep_realloc(pattern, ++pattern_sz * + sizeof(*pattern)); + } + if (len > 0 && pat[len - 1] == '\n') + --len; + /* pat may not be NUL-terminated */ + pattern[patterns] = grep_malloc(len + 1); + memcpy(pattern[patterns], pat, len); + pattern[patterns][len] = '\0'; + ++patterns; +} + +/* + * Adds a file include/exclude pattern to the internal array. + */ +static void +add_fpattern(const char *pat, int mode) +{ + + /* Increase size if necessary */ + if (fpatterns == fpattern_sz) { + fpattern_sz *= 2; + fpattern = grep_realloc(fpattern, ++fpattern_sz * + sizeof(struct epat)); + } + fpattern[fpatterns].pat = grep_strdup(pat); + fpattern[fpatterns].mode = mode; + ++fpatterns; +} + +/* + * Adds a directory include/exclude pattern to the internal array. + */ +static void +add_dpattern(const char *pat, int mode) +{ + + /* Increase size if necessary */ + if (dpatterns == dpattern_sz) { + dpattern_sz *= 2; + dpattern = grep_realloc(dpattern, ++dpattern_sz * + sizeof(struct epat)); + } + dpattern[dpatterns].pat = grep_strdup(pat); + dpattern[dpatterns].mode = mode; + ++dpatterns; +} + +/* + * Reads searching patterns from a file and adds them with add_pattern(). + */ +static void +read_patterns(const char *fn) +{ + FILE *f; + char *line; + size_t len; + ssize_t rlen; + + if ((f = fopen(fn, "r")) == NULL) + err(2, "%s", fn); + line = NULL; + len = 0; +#ifndef ANDROID + while ((rlen = getline(&line, &len, f)) != -1) + add_pattern(line, *line == '\n' ? 0 : (size_t)rlen); +#endif + free(line); + if (ferror(f)) + err(2, "%s", fn); + fclose(f); +} + +static inline const char * +init_color(const char *d) +{ + char *c; + + c = getenv("GREP_COLOR"); + return (c != NULL ? c : d); +} + +int +grep_main(int argc, char *argv[]) +{ + char **aargv, **eargv, *eopts; + char *ep; + unsigned long long l; + unsigned int aargc, eargc, i, j; + int c, lastc, needpattern, newarg, prevoptind; + + setlocale(LC_ALL, ""); + +#ifndef WITHOUT_NLS + catalog = catopen("grep", NL_CAT_LOCALE); +#endif + + /* Check what is the program name of the binary. In this + way we can have all the funcionalities in one binary + without the need of scripting and using ugly hacks. */ + switch (__progname[0]) { + case 'e': + grepbehave = GREP_EXTENDED; + break; + case 'f': + grepbehave = GREP_FIXED; + break; + case 'g': + grepbehave = GREP_BASIC; + break; + case 'z': + filebehave = FILE_GZIP; + switch(__progname[1]) { + case 'e': + grepbehave = GREP_EXTENDED; + break; + case 'f': + grepbehave = GREP_FIXED; + break; + case 'g': + grepbehave = GREP_BASIC; + break; + } + break; + } + + lastc = '\0'; + newarg = 1; + prevoptind = 1; + needpattern = 1; + + eopts = getenv("GREP_OPTIONS"); + + /* support for extra arguments in GREP_OPTIONS */ + eargc = 0; + if (eopts != NULL) { + char *str; + + /* make an estimation of how many extra arguments we have */ + for (j = 0; j < strlen(eopts); j++) + if (eopts[j] == ' ') + eargc++; + + eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1)); + + eargc = 0; + /* parse extra arguments */ + while ((str = strsep(&eopts, " ")) != NULL) + eargv[eargc++] = grep_strdup(str); + + aargv = (char **)grep_calloc(eargc + argc + 1, + sizeof(char *)); + + aargv[0] = argv[0]; + for (i = 0; i < eargc; i++) + aargv[i + 1] = eargv[i]; + for (j = 1; j < (unsigned int)argc; j++, i++) + aargv[i + 1] = argv[j]; + + aargc = eargc + argc; + } else { + aargv = argv; + aargc = argc; + } + + while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) != + -1)) { + switch (c) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + if (newarg || !isdigit(lastc)) + Aflag = 0; + else if (Aflag > LLONG_MAX / 10) { + errno = ERANGE; + err(2, NULL); + } + Aflag = Bflag = (Aflag * 10) + (c - '0'); + break; + case 'C': + if (optarg == NULL) { + Aflag = Bflag = 2; + break; + } + /* FALLTHROUGH */ + case 'A': + /* FALLTHROUGH */ + case 'B': + errno = 0; + l = strtoull(optarg, &ep, 10); + if (((errno == ERANGE) && (l == ULLONG_MAX)) || + ((errno == EINVAL) && (l == 0))) + err(2, NULL); + else if (ep[0] != '\0') { + errno = EINVAL; + err(2, NULL); + } + if (c == 'A') + Aflag = l; + else if (c == 'B') + Bflag = l; + else + Aflag = Bflag = l; + break; + case 'a': + binbehave = BINFILE_TEXT; + break; + case 'b': + bflag = true; + break; + case 'c': + cflag = true; + break; + case 'D': + if (strcasecmp(optarg, "skip") == 0) + devbehave = DEV_SKIP; + else if (strcasecmp(optarg, "read") == 0) + devbehave = DEV_READ; + else + errx(2, getstr(3), "--devices"); + break; + case 'd': + if (strcasecmp("recurse", optarg) == 0) { + Hflag = true; + dirbehave = DIR_RECURSE; + } else if (strcasecmp("skip", optarg) == 0) + dirbehave = DIR_SKIP; + else if (strcasecmp("read", optarg) == 0) + dirbehave = DIR_READ; + else + errx(2, getstr(3), "--directories"); + break; + case 'E': + grepbehave = GREP_EXTENDED; + break; + case 'e': + add_pattern(optarg, strlen(optarg)); + needpattern = 0; + break; + case 'F': + grepbehave = GREP_FIXED; + break; + case 'f': + read_patterns(optarg); + needpattern = 0; + break; + case 'G': + grepbehave = GREP_BASIC; + break; + case 'H': + Hflag = true; + break; + case 'h': + Hflag = false; + hflag = true; + break; + case 'I': + binbehave = BINFILE_SKIP; + break; + case 'i': + case 'y': + iflag = true; + cflags |= REG_ICASE; + break; + case 'J': + filebehave = FILE_BZIP; + break; + case 'L': + lflag = false; + Lflag = true; + break; + case 'l': + Lflag = false; + lflag = true; + break; + case 'm': + mflag = true; + errno = 0; + mcount = strtoull(optarg, &ep, 10); + if (((errno == ERANGE) && (mcount == ULLONG_MAX)) || + ((errno == EINVAL) && (mcount == 0))) + err(2, NULL); + else if (ep[0] != '\0') { + errno = EINVAL; + err(2, NULL); + } + break; + case 'n': + nflag = true; + break; + case 'O': + linkbehave = LINK_EXPLICIT; + break; + case 'o': + oflag = true; + break; + case 'p': + linkbehave = LINK_SKIP; + break; + case 'q': + qflag = true; + break; + case 'S': + linkbehave = LINK_READ; + break; + case 'R': + case 'r': + dirbehave = DIR_RECURSE; + Hflag = true; + break; + case 's': + sflag = true; + break; + case 'U': + binbehave = BINFILE_BIN; + break; + case 'u': + case MMAP_OPT: + /* noop, compatibility */ + break; + case 'V': + printf(getstr(9), __progname, VERSION); + exit(0); + case 'v': + vflag = true; + break; + case 'w': + wflag = true; + break; + case 'x': + xflag = true; + break; + case 'Z': + nullflag = true; + break; + case 'z': + nulldataflag = true; + line_sep = '\0'; + break; + case BIN_OPT: + if (strcasecmp("binary", optarg) == 0) + binbehave = BINFILE_BIN; + else if (strcasecmp("without-match", optarg) == 0) + binbehave = BINFILE_SKIP; + else if (strcasecmp("text", optarg) == 0) + binbehave = BINFILE_TEXT; + else + errx(2, getstr(3), "--binary-files"); + break; + case COLOR_OPT: + color = NULL; + if (optarg == NULL || strcasecmp("auto", optarg) == 0 || + strcasecmp("tty", optarg) == 0 || + strcasecmp("if-tty", optarg) == 0) { + char *term; + + term = getenv("TERM"); + if (isatty(STDOUT_FILENO) && term != NULL && + strcasecmp(term, "dumb") != 0) + color = init_color("01;31"); + } else if (strcasecmp("always", optarg) == 0 || + strcasecmp("yes", optarg) == 0 || + strcasecmp("force", optarg) == 0) { + color = init_color("01;31"); + } else if (strcasecmp("never", optarg) != 0 && + strcasecmp("none", optarg) != 0 && + strcasecmp("no", optarg) != 0) + errx(2, getstr(3), "--color"); + break; + case DECOMPRESS_OPT: + filebehave = FILE_GZIP; + break; + case LABEL_OPT: + label = optarg; + break; + case LINEBUF_OPT: + lbflag = true; + break; + case R_INCLUDE_OPT: + finclude = true; + add_fpattern(optarg, INCL_PAT); + break; + case R_EXCLUDE_OPT: + fexclude = true; + add_fpattern(optarg, EXCL_PAT); + break; + case R_DINCLUDE_OPT: + dinclude = true; + add_dpattern(optarg, INCL_PAT); + break; + case R_DEXCLUDE_OPT: + dexclude = true; + add_dpattern(optarg, EXCL_PAT); + break; + case HELP_OPT: + default: + usage(); + } + lastc = c; + newarg = optind != prevoptind; + prevoptind = optind; + } + aargc -= optind; + aargv += optind; + + /* Fail if we don't have any pattern */ + if (aargc == 0 && needpattern) + usage(); + + /* Process patterns from command line */ + if (aargc != 0 && needpattern) { + add_pattern(*aargv, strlen(*aargv)); + --aargc; + ++aargv; + } + + switch (grepbehave) { + case GREP_FIXED: + case GREP_BASIC: + break; + case GREP_EXTENDED: + cflags |= REG_EXTENDED; + break; + default: + /* NOTREACHED */ + usage(); + } + + fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern)); + r_pattern = grep_calloc(patterns, sizeof(*r_pattern)); +/* + * XXX: fgrepcomp() and fastcomp() are workarounds for regexec() performance. + * Optimizations should be done there. + */ + /* Check if cheating is allowed (always is for fgrep). */ + if (grepbehave == GREP_FIXED) { + for (i = 0; i < patterns; ++i) + fgrepcomp(&fg_pattern[i], pattern[i]); + } else { + for (i = 0; i < patterns; ++i) { + if (fastcomp(&fg_pattern[i], pattern[i])) { + /* Fall back to full regex library */ + c = regcomp(&r_pattern[i], pattern[i], cflags); + if (c != 0) { + regerror(c, &r_pattern[i], re_error, + RE_ERROR_BUF); + errx(2, "%s", re_error); + } + } + } + } + + if (lbflag) + setlinebuf(stdout); + + if ((aargc == 0 || aargc == 1) && !Hflag) + hflag = true; + + if (aargc == 0) + exit(!procfile("-")); + + if (dirbehave == DIR_RECURSE) + c = grep_tree(aargv); + else + for (c = 0; aargc--; ++aargv) { + if ((finclude || fexclude) && !file_matching(*aargv)) + continue; + c+= procfile(*aargv); + } + +#ifndef WITHOUT_NLS + catclose(catalog); +#endif + + /* Find out the correct return value according to the + results and the command line option. */ + exit(c ? (notfound ? (qflag ? 0 : 2) : 0) : (notfound ? 2 : 1)); +} diff --git a/toolbox/grep/grep.h b/toolbox/grep/grep.h new file mode 100644 index 0000000..6454f93 --- /dev/null +++ b/toolbox/grep/grep.h @@ -0,0 +1,166 @@ +/* $NetBSD: grep.h,v 1.8 2012/05/06 22:27:00 joerg Exp $ */ +/* $OpenBSD: grep.h,v 1.15 2010/04/05 03:03:55 tedu Exp $ */ +/* $FreeBSD: head/usr.bin/grep/grep.h 211496 2010-08-19 09:28:59Z des $ */ + +/*- + * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav + * Copyright (c) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org> + * All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifdef ANDROID +#define WITHOUT_NLS +#endif + +#ifndef ANDROID +#include <bzlib.h> +#endif +#include <limits.h> +#include <regex.h> +#include <stdbool.h> +#include <stdio.h> +#ifndef ANDROID +#include <zlib.h> +#endif + +#ifdef WITHOUT_NLS +#define getstr(n) errstr[n] +#else +#include <nl_types.h> + +extern nl_catd catalog; +#define getstr(n) catgets(catalog, 1, n, errstr[n]) +#endif + +extern const char *errstr[]; + +#define VERSION "2.5.1-FreeBSD" + +#define GREP_FIXED 0 +#define GREP_BASIC 1 +#define GREP_EXTENDED 2 + +#define BINFILE_BIN 0 +#define BINFILE_SKIP 1 +#define BINFILE_TEXT 2 + +#define FILE_STDIO 0 +#define FILE_GZIP 1 +#define FILE_BZIP 2 + +#define DIR_READ 0 +#define DIR_SKIP 1 +#define DIR_RECURSE 2 + +#define DEV_READ 0 +#define DEV_SKIP 1 + +#define LINK_READ 0 +#define LINK_EXPLICIT 1 +#define LINK_SKIP 2 + +#define EXCL_PAT 0 +#define INCL_PAT 1 + +#define MAX_LINE_MATCHES 32 + +struct file { + int fd; + bool binary; +}; + +struct str { + off_t off; + size_t len; + char *dat; + char *file; + int line_no; +}; + +struct epat { + char *pat; + int mode; +}; + +typedef struct { + size_t len; + unsigned char *pattern; + int qsBc[UCHAR_MAX + 1]; + /* flags */ + bool bol; + bool eol; + bool reversed; + bool word; +} fastgrep_t; + +/* Flags passed to regcomp() and regexec() */ +extern int cflags, eflags; + +/* Command line flags */ +extern bool Eflag, Fflag, Gflag, Hflag, Lflag, + bflag, cflag, hflag, iflag, lflag, mflag, nflag, oflag, + qflag, sflag, vflag, wflag, xflag; +extern bool dexclude, dinclude, fexclude, finclude, lbflag, nullflag, nulldataflag; +extern unsigned char line_sep; +extern unsigned long long Aflag, Bflag, mcount; +extern char *label; +extern const char *color; +extern int binbehave, devbehave, dirbehave, filebehave, grepbehave, linkbehave; + +extern bool notfound; +extern int tail; +extern unsigned int dpatterns, fpatterns, patterns; +extern char **pattern; +extern struct epat *dpattern, *fpattern; +extern regex_t *er_pattern, *r_pattern; +extern fastgrep_t *fg_pattern; + +/* For regex errors */ +#define RE_ERROR_BUF 512 +extern char re_error[RE_ERROR_BUF + 1]; /* Seems big enough */ + +/* util.c */ +bool file_matching(const char *fname); +int procfile(const char *fn); +int grep_tree(char **argv); +void *grep_malloc(size_t size); +void *grep_calloc(size_t nmemb, size_t size); +void *grep_realloc(void *ptr, size_t size); +char *grep_strdup(const char *str); +void printline(struct str *line, int sep, regmatch_t *matches, int m); + +/* queue.c */ +void enqueue(struct str *x); +void printqueue(void); +void clearqueue(void); + +/* file.c */ +void grep_close(struct file *f); +struct file *grep_open(const char *path); +char *grep_fgetln(struct file *f, size_t *len); + +/* fastgrep.c */ +int fastcomp(fastgrep_t *, const char *); +void fgrepcomp(fastgrep_t *, const char *); +int grep_search(fastgrep_t *, const unsigned char *, size_t, regmatch_t *); diff --git a/toolbox/grep/queue.c b/toolbox/grep/queue.c new file mode 100644 index 0000000..e3c6be1 --- /dev/null +++ b/toolbox/grep/queue.c @@ -0,0 +1,116 @@ +/* $NetBSD: queue.c,v 1.5 2011/08/31 16:24:57 plunky Exp $ */ +/* $FreeBSD: head/usr.bin/grep/queue.c 211496 2010-08-19 09:28:59Z des $ */ +/*- + * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav + * All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * A really poor man's queue. It does only what it has to and gets out of + * Dodge. It is used in place of <sys/queue.h> to get a better performance. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +__RCSID("$NetBSD: queue.c,v 1.5 2011/08/31 16:24:57 plunky Exp $"); + +#include <sys/param.h> +#include <sys/queue.h> + +#include <stdlib.h> +#include <string.h> + +#include "grep.h" + +struct qentry { + STAILQ_ENTRY(qentry) list; + struct str data; +}; + +static STAILQ_HEAD(, qentry) queue = STAILQ_HEAD_INITIALIZER(queue); +static unsigned long long count; + +static struct qentry *dequeue(void); + +void +enqueue(struct str *x) +{ + struct qentry *item; + + item = grep_malloc(sizeof(struct qentry)); + item->data.dat = grep_malloc(sizeof(char) * x->len); + item->data.len = x->len; + item->data.line_no = x->line_no; + item->data.off = x->off; + memcpy(item->data.dat, x->dat, x->len); + item->data.file = x->file; + + STAILQ_INSERT_TAIL(&queue, item, list); + + if (++count > Bflag) { + item = dequeue(); + free(item->data.dat); + free(item); + } +} + +static struct qentry * +dequeue(void) +{ + struct qentry *item; + + item = STAILQ_FIRST(&queue); + if (item == NULL) + return (NULL); + + STAILQ_REMOVE_HEAD(&queue, list); + --count; + return (item); +} + +void +printqueue(void) +{ + struct qentry *item; + + while ((item = dequeue()) != NULL) { + printline(&item->data, '-', NULL, 0); + free(item->data.dat); + free(item); + } +} + +void +clearqueue(void) +{ + struct qentry *item; + + while ((item = dequeue()) != NULL) { + free(item->data.dat); + free(item); + } +} diff --git a/toolbox/grep/util.c b/toolbox/grep/util.c new file mode 100644 index 0000000..497db06 --- /dev/null +++ b/toolbox/grep/util.c @@ -0,0 +1,498 @@ +/* $NetBSD: util.c,v 1.16 2012/05/06 22:32:05 joerg Exp $ */ +/* $FreeBSD: head/usr.bin/grep/util.c 211496 2010-08-19 09:28:59Z des $ */ +/* $OpenBSD: util.c,v 1.39 2010/07/02 22:18:03 tedu Exp $ */ + +/*- + * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav + * Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org> + * All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +__RCSID("$NetBSD: util.c,v 1.16 2012/05/06 22:32:05 joerg Exp $"); + +#include <sys/stat.h> +#include <sys/types.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fnmatch.h> +#include <fts.h> +#include <libgen.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <wchar.h> +#include <wctype.h> + +#include "grep.h" + +static bool first, first_global = true; +static unsigned long long since_printed; + +static int procline(struct str *l, int); + +bool +file_matching(const char *fname) +{ + char *fname_base, *fname_copy; + unsigned int i; + bool ret; + + ret = finclude ? false : true; + fname_copy = grep_strdup(fname); + fname_base = basename(fname_copy); + + for (i = 0; i < fpatterns; ++i) { + if (fnmatch(fpattern[i].pat, fname, 0) == 0 || + fnmatch(fpattern[i].pat, fname_base, 0) == 0) { + if (fpattern[i].mode == EXCL_PAT) + return (false); + else + ret = true; + } + } + free(fname_copy); + return (ret); +} + +static inline bool +dir_matching(const char *dname) +{ + unsigned int i; + bool ret; + + ret = dinclude ? false : true; + + for (i = 0; i < dpatterns; ++i) { + if (dname != NULL && + fnmatch(dname, dpattern[i].pat, 0) == 0) { + if (dpattern[i].mode == EXCL_PAT) + return (false); + else + ret = true; + } + } + return (ret); +} + +/* + * Processes a directory when a recursive search is performed with + * the -R option. Each appropriate file is passed to procfile(). + */ +int +grep_tree(char **argv) +{ + FTS *fts; + FTSENT *p; + char *d, *dir = NULL; + int c, fts_flags; + bool ok; + + c = fts_flags = 0; + + switch(linkbehave) { + case LINK_EXPLICIT: + fts_flags = FTS_COMFOLLOW; + break; + case LINK_SKIP: + fts_flags = FTS_PHYSICAL; + break; + default: + fts_flags = FTS_LOGICAL; + + } + + fts_flags |= FTS_NOSTAT | FTS_NOCHDIR; + + if (!(fts = fts_open(argv, fts_flags, NULL))) + err(2, "fts_open"); + while ((p = fts_read(fts)) != NULL) { + switch (p->fts_info) { + case FTS_DNR: + /* FALLTHROUGH */ + case FTS_ERR: + errx(2, "%s: %s", p->fts_path, strerror(p->fts_errno)); + break; + case FTS_D: + /* FALLTHROUGH */ + case FTS_DP: + break; + case FTS_DC: + /* Print a warning for recursive directory loop */ + warnx("warning: %s: recursive directory loop", + p->fts_path); + break; + default: + /* Check for file exclusion/inclusion */ + ok = true; + if (dexclude || dinclude) { + if ((d = strrchr(p->fts_path, '/')) != NULL) { + dir = grep_malloc(sizeof(char) * + (d - p->fts_path + 1)); + memcpy(dir, p->fts_path, + d - p->fts_path); + dir[d - p->fts_path] = '\0'; + } + ok = dir_matching(dir); + free(dir); + dir = NULL; + } + if (fexclude || finclude) + ok &= file_matching(p->fts_path); + + if (ok) + c += procfile(p->fts_path); + break; + } + } + + fts_close(fts); + return (c); +} + +/* + * Opens a file and processes it. Each file is processed line-by-line + * passing the lines to procline(). + */ +int +procfile(const char *fn) +{ + struct file *f; + struct stat sb; + struct str ln; + mode_t s; + int c, t; + + if (mflag && (mcount <= 0)) + return (0); + + if (strcmp(fn, "-") == 0) { + fn = label != NULL ? label : getstr(1); + f = grep_open(NULL); + } else { + if (!stat(fn, &sb)) { + /* Check if we need to process the file */ + s = sb.st_mode & S_IFMT; + if (s == S_IFDIR && dirbehave == DIR_SKIP) + return (0); + if ((s == S_IFIFO || s == S_IFCHR || s == S_IFBLK + || s == S_IFSOCK) && devbehave == DEV_SKIP) + return (0); + } + f = grep_open(fn); + } + if (f == NULL) { + if (!sflag) + warn("%s", fn); + if (errno == ENOENT) + notfound = true; + return (0); + } + + ln.file = grep_malloc(strlen(fn) + 1); + strcpy(ln.file, fn); + ln.line_no = 0; + ln.len = 0; + tail = 0; + ln.off = -1; + + for (first = true, c = 0; c == 0 || !(lflag || qflag); ) { + ln.off += ln.len + 1; + if ((ln.dat = grep_fgetln(f, &ln.len)) == NULL || ln.len == 0) + break; + if (ln.len > 0 && ln.dat[ln.len - 1] == line_sep) + --ln.len; + ln.line_no++; + + /* Return if we need to skip a binary file */ + if (f->binary && binbehave == BINFILE_SKIP) { + grep_close(f); + free(ln.file); + free(f); + return (0); + } + /* Process the file line-by-line */ + t = procline(&ln, f->binary); + c += t; + + /* Count the matches if we have a match limit */ + if (mflag) { + mcount -= t; + if (mcount <= 0) + break; + } + } + if (Bflag > 0) + clearqueue(); + grep_close(f); + + if (cflag) { + if (!hflag) + printf("%s:", ln.file); + printf("%u%c", c, line_sep); + } + if (lflag && !qflag && c != 0) + printf("%s%c", fn, line_sep); + if (Lflag && !qflag && c == 0) + printf("%s%c", fn, line_sep); + if (c && !cflag && !lflag && !Lflag && + binbehave == BINFILE_BIN && f->binary && !qflag) + printf(getstr(8), fn); + + free(ln.file); + free(f); + return (c); +} + +#define iswword(x) (iswalnum((x)) || (x) == L'_') + +/* + * Processes a line comparing it with the specified patterns. Each pattern + * is looped to be compared along with the full string, saving each and every + * match, which is necessary to colorize the output and to count the + * matches. The matching lines are passed to printline() to display the + * appropriate output. + */ +static int +procline(struct str *l, int nottext) +{ + regmatch_t matches[MAX_LINE_MATCHES]; + regmatch_t pmatch; + size_t st = 0; + unsigned int i; + int c = 0, m = 0, r = 0; + + /* Loop to process the whole line */ + while (st <= l->len) { + pmatch.rm_so = st; + pmatch.rm_eo = l->len; + + /* Loop to compare with all the patterns */ + for (i = 0; i < patterns; i++) { +/* + * XXX: grep_search() is a workaround for speed up and should be + * removed in the future. See fastgrep.c. + */ + if (fg_pattern[i].pattern) { + r = grep_search(&fg_pattern[i], + (unsigned char *)l->dat, + l->len, &pmatch); + r = (r == 0) ? 0 : REG_NOMATCH; + st = pmatch.rm_eo; + } else { + r = regexec(&r_pattern[i], l->dat, 1, + &pmatch, eflags); + r = (r == 0) ? 0 : REG_NOMATCH; + st = pmatch.rm_eo; + } + if (r == REG_NOMATCH) + continue; + /* Check for full match */ + if (xflag && + (pmatch.rm_so != 0 || + (size_t)pmatch.rm_eo != l->len)) + continue; + /* Check for whole word match */ + if (fg_pattern[i].word && pmatch.rm_so != 0) { + wint_t wbegin, wend; + + wbegin = wend = L' '; + if (pmatch.rm_so != 0 && + sscanf(&l->dat[pmatch.rm_so - 1], + "%lc", &wbegin) != 1) + continue; + if ((size_t)pmatch.rm_eo != l->len && + sscanf(&l->dat[pmatch.rm_eo], + "%lc", &wend) != 1) + continue; + if (iswword(wbegin) || iswword(wend)) + continue; + } + c = 1; + if (m < MAX_LINE_MATCHES) + matches[m++] = pmatch; + /* matches - skip further patterns */ + if ((color != NULL && !oflag) || qflag || lflag) + break; + } + + if (vflag) { + c = !c; + break; + } + /* One pass if we are not recording matches */ + if ((color != NULL && !oflag) || qflag || lflag) + break; + + if (st == (size_t)pmatch.rm_so) + break; /* No matches */ + } + + if (c && binbehave == BINFILE_BIN && nottext) + return (c); /* Binary file */ + + /* Dealing with the context */ + if ((tail || c) && !cflag && !qflag && !lflag && !Lflag) { + if (c) { + if ((Aflag || Bflag) && !first_global && + (first || since_printed > Bflag)) + printf("--\n"); + tail = Aflag; + if (Bflag > 0) + printqueue(); + printline(l, ':', matches, m); + } else { + printline(l, '-', matches, m); + tail--; + } + first = false; + first_global = false; + since_printed = 0; + } else { + if (Bflag) + enqueue(l); + since_printed++; + } + return (c); +} + +/* + * Safe malloc() for internal use. + */ +void * +grep_malloc(size_t size) +{ + void *ptr; + + if ((ptr = malloc(size)) == NULL) + err(2, "malloc"); + return (ptr); +} + +/* + * Safe calloc() for internal use. + */ +void * +grep_calloc(size_t nmemb, size_t size) +{ + void *ptr; + + if ((ptr = calloc(nmemb, size)) == NULL) + err(2, "calloc"); + return (ptr); +} + +/* + * Safe realloc() for internal use. + */ +void * +grep_realloc(void *ptr, size_t size) +{ + + if ((ptr = realloc(ptr, size)) == NULL) + err(2, "realloc"); + return (ptr); +} + +/* + * Safe strdup() for internal use. + */ +char * +grep_strdup(const char *str) +{ + char *ret; + + if ((ret = strdup(str)) == NULL) + err(2, "strdup"); + return (ret); +} + +/* + * Prints a matching line according to the command line options. + */ +void +printline(struct str *line, int sep, regmatch_t *matches, int m) +{ + size_t a = 0; + int i, n = 0; + + if (!hflag) { + if (nullflag == 0) + fputs(line->file, stdout); + else { + printf("%s", line->file); + putchar(0); + } + ++n; + } + if (nflag) { + if (n > 0) + putchar(sep); + printf("%d", line->line_no); + ++n; + } + if (bflag) { + if (n > 0) + putchar(sep); + printf("%lld", (long long)line->off); + ++n; + } + if (n) + putchar(sep); + /* --color and -o */ + if ((oflag || color) && m > 0) { + for (i = 0; i < m; i++) { + if (!oflag) + fwrite(line->dat + a, matches[i].rm_so - a, 1, + stdout); + if (color) + fprintf(stdout, "\33[%sm\33[K", color); + + fwrite(line->dat + matches[i].rm_so, + matches[i].rm_eo - matches[i].rm_so, 1, + stdout); + if (color) + fprintf(stdout, "\33[m\33[K"); + a = matches[i].rm_eo; + if (oflag) + putchar('\n'); + } + if (!oflag) { + if (line->len - a > 0) + fwrite(line->dat + a, line->len - a, 1, stdout); + putchar(line_sep); + } + } else { + fwrite(line->dat, line->len, 1, stdout); + putchar(line_sep); + } +} diff --git a/toolbox/id.c b/toolbox/id.c index bc79288..8ec79c1 100644 --- a/toolbox/id.c +++ b/toolbox/id.c @@ -4,10 +4,7 @@ #include <sys/types.h> #include <pwd.h> #include <grp.h> - -#ifdef HAVE_SELINUX #include <selinux/selinux.h> -#endif static void print_uid(uid_t uid) { @@ -34,9 +31,7 @@ int id_main(int argc, char **argv) { gid_t list[64]; int n, max; -#ifdef HAVE_SELINUX char *secctx; -#endif max = getgroups(64, list); if (max < 0) max = 0; @@ -53,12 +48,10 @@ int id_main(int argc, char **argv) print_gid(list[n]); } } -#ifdef HAVE_SELINUX if (getcon(&secctx) == 0) { printf(" context=%s", secctx); free(secctx); } -#endif printf("\n"); return 0; } diff --git a/toolbox/ls.c b/toolbox/ls.c index a4db99c..e530521 100644 --- a/toolbox/ls.c +++ b/toolbox/ls.c @@ -5,9 +5,7 @@ #include <dirent.h> #include <errno.h> -#ifdef HAVE_SELINUX #include <selinux/selinux.h> -#endif #include <sys/stat.h> #include <unistd.h> @@ -260,11 +258,7 @@ static int listfile_maclabel(const char *path, int flags) return -1; } -#ifdef HAVE_SELINUX lgetfilecon(path, &maclabel); -#else - maclabel = strdup("-"); -#endif if (!maclabel) { return -1; } @@ -276,12 +270,12 @@ static int listfile_maclabel(const char *path, int flags) switch(s.st_mode & S_IFMT) { case S_IFLNK: { char linkto[256]; - int len; + ssize_t len; len = readlink(path, linkto, sizeof(linkto)); if(len < 0) return -1; - if(len > sizeof(linkto)-1) { + if((size_t)len > sizeof(linkto)-1) { linkto[sizeof(linkto)-4] = '.'; linkto[sizeof(linkto)-3] = '.'; linkto[sizeof(linkto)-2] = '.'; @@ -307,7 +301,7 @@ static int listfile_maclabel(const char *path, int flags) static int listfile(const char *dirname, const char *filename, int flags) { - if ((flags & LIST_LONG | LIST_SIZE | LIST_CLASSIFY | LIST_MACLABEL) == 0) { + if ((flags & (LIST_LONG | LIST_SIZE | LIST_CLASSIFY | LIST_MACLABEL)) == 0) { printf("%s\n", filename); return 0; } diff --git a/toolbox/mount.c b/toolbox/mount.c index 27cf3c9..b7adce2 100644 --- a/toolbox/mount.c +++ b/toolbox/mount.c @@ -49,12 +49,17 @@ static const struct mount_opts options[] = { { "exec", MS_NOEXEC, 0, MS_NOEXEC }, { "move", MS_TYPE, MS_MOVE, 0 }, { "recurse", MS_REC, MS_REC, 0 }, + { "rec", MS_REC, MS_REC, 0 }, { "remount", MS_TYPE, MS_REMOUNT, 0 }, { "ro", MS_RDONLY, MS_RDONLY, 0 }, { "rw", MS_RDONLY, 0, MS_RDONLY }, { "suid", MS_NOSUID, 0, MS_NOSUID }, { "sync", MS_SYNCHRONOUS, MS_SYNCHRONOUS, 0 }, { "verbose", MS_VERBOSE, MS_VERBOSE, 0 }, + { "unbindable", MS_UNBINDABLE, MS_UNBINDABLE, 0 }, + { "private", MS_PRIVATE, MS_PRIVATE, 0 }, + { "slave", MS_SLAVE, MS_SLAVE, 0 }, + { "shared", MS_SHARED, MS_SHARED, 0 }, }; static void add_extra_option(struct extra_opts *extra, char *s) diff --git a/toolbox/setsebool.c b/toolbox/setsebool.c index 4a3d87d..f79a612 100644 --- a/toolbox/setsebool.c +++ b/toolbox/setsebool.c @@ -9,35 +9,26 @@ #include <errno.h> static int do_setsebool(int nargs, char **args) { - SELboolean *b = alloca(nargs * sizeof(SELboolean)); - char *v; - int i; + const char *name = args[1]; + const char *value = args[2]; + SELboolean b; if (is_selinux_enabled() <= 0) return 0; - for (i = 1; i < nargs; i++) { - char *name = args[i]; - v = strchr(name, '='); - if (!v) { - fprintf(stderr, "setsebool: argument %s had no =\n", name); - return -1; - } - *v++ = 0; - b[i-1].name = name; - if (!strcmp(v, "1") || !strcasecmp(v, "true") || !strcasecmp(v, "on")) - b[i-1].value = 1; - else if (!strcmp(v, "0") || !strcasecmp(v, "false") || !strcasecmp(v, "off")) - b[i-1].value = 0; - else { - fprintf(stderr, "setsebool: invalid value %s\n", v); - return -1; - } + b.name = name; + if (!strcmp(value, "1") || !strcasecmp(value, "true") || !strcasecmp(value, "on")) + b.value = 1; + else if (!strcmp(value, "0") || !strcasecmp(value, "false") || !strcasecmp(value, "off")) + b.value = 0; + else { + fprintf(stderr, "setsebool: invalid value %s\n", value); + return -1; } - if (security_set_boolean_list(nargs - 1, b, 0) < 0) + if (security_set_boolean_list(1, &b, 0) < 0) { - fprintf(stderr, "setsebool: unable to set booleans: %s", strerror(errno)); + fprintf(stderr, "setsebool: could not set %s to %s: %s", name, value, strerror(errno)); return -1; } @@ -46,8 +37,8 @@ static int do_setsebool(int nargs, char **args) { int setsebool_main(int argc, char **argv) { - if (argc < 2) { - fprintf(stderr, "Usage: %s name=value...\n", argv[0]); + if (argc != 3) { + fprintf(stderr, "Usage: %s name value\n", argv[0]); exit(1); } |