diff options
388 files changed, 28536 insertions, 30790 deletions
diff --git a/adb/Android.mk b/adb/Android.mk index 721b48d..62f012c 100644 --- a/adb/Android.mk +++ b/adb/Android.mk @@ -34,7 +34,7 @@ endif ifeq ($(HOST_OS),windows) USB_SRCS := usb_windows.c - EXTRA_SRCS := get_my_path_windows.c ../libcutils/list.c + EXTRA_SRCS := get_my_path_windows.c EXTRA_STATIC_LIBS := AdbWinApi ifneq ($(strip $(USE_CYGWIN)),) # Pure cygwin case @@ -114,8 +114,7 @@ LOCAL_SRC_FILES := \ jdwp_service.c \ framebuffer_service.c \ remount_service.c \ - usb_linux_client.c \ - log_service.c + usb_linux_client.c LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE @@ -130,7 +129,7 @@ LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN) LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED) -LOCAL_STATIC_LIBRARIES := liblog libcutils libc libmincrypt +LOCAL_STATIC_LIBRARIES := liblog libcutils libc libmincrypt libselinux include $(BUILD_EXECUTABLE) diff --git a/adb/SERVICES.TXT b/adb/SERVICES.TXT index 5966686..7f85dc3 100644 --- a/adb/SERVICES.TXT +++ b/adb/SERVICES.TXT @@ -198,11 +198,6 @@ localfilesystem:<path> Variants of local:<path> that are used to access other Android socket namespaces. -log:<name> - Opens one of the system logs (/dev/log/<name>) and allows the client - to read them directly. Used to implement 'adb logcat'. The stream - will be read-only for the client. - framebuffer: This service is used to send snapshots of the framebuffer to a client. It requires sufficient privileges but works as follow: @@ -39,6 +39,8 @@ #include <sys/capability.h> #include <linux/prctl.h> #include <sys/mount.h> +#include <getopt.h> +#include <selinux/selinux.h> #else #include "usb_vendors.h" #endif @@ -54,6 +56,7 @@ static int auth_enabled = 0; #if !ADB_HOST static const char *adb_device_banner = "device"; +static const char *root_seclabel = NULL; #endif void fatal(const char *fmt, ...) @@ -562,7 +565,7 @@ void handle_packet(apacket *p, atransport *t) break; case A_OPEN: /* OPEN(local-id, 0, "destination") */ - if (t->online) { + if (t->online && p->msg.arg0 != 0 && p->msg.arg1 == 0) { char *name = (char*) p->data; name[p->msg.data_length > 0 ? p->msg.data_length - 1 : 0] = 0; s = create_local_service_socket(name); @@ -578,28 +581,50 @@ void handle_packet(apacket *p, atransport *t) break; case A_OKAY: /* READY(local-id, remote-id, "") */ - if (t->online) { - if((s = find_local_socket(p->msg.arg1))) { + if (t->online && p->msg.arg0 != 0 && p->msg.arg1 != 0) { + if((s = find_local_socket(p->msg.arg1, 0))) { if(s->peer == 0) { + /* On first READY message, create the connection. */ s->peer = create_remote_socket(p->msg.arg0, t); s->peer->peer = s; + s->ready(s); + } else if (s->peer->id == p->msg.arg0) { + /* Other READY messages must use the same local-id */ + s->ready(s); + } else { + D("Invalid A_OKAY(%d,%d), expected A_OKAY(%d,%d) on transport %s\n", + p->msg.arg0, p->msg.arg1, s->peer->id, p->msg.arg1, t->serial); } - s->ready(s); } } break; - case A_CLSE: /* CLOSE(local-id, remote-id, "") */ - if (t->online) { - if((s = find_local_socket(p->msg.arg1))) { - s->close(s); + case A_CLSE: /* CLOSE(local-id, remote-id, "") or CLOSE(0, remote-id, "") */ + if (t->online && p->msg.arg1 != 0) { + if((s = find_local_socket(p->msg.arg1, p->msg.arg0))) { + /* According to protocol.txt, p->msg.arg0 might be 0 to indicate + * a failed OPEN only. However, due to a bug in previous ADB + * versions, CLOSE(0, remote-id, "") was also used for normal + * CLOSE() operations. + * + * This is bad because it means a compromised adbd could + * send packets to close connections between the host and + * other devices. To avoid this, only allow this if the local + * socket has a peer on the same transport. + */ + if (p->msg.arg0 == 0 && s->peer && s->peer->transport != t) { + D("Invalid A_CLSE(0, %u) from transport %s, expected transport %s\n", + p->msg.arg1, t->serial, s->peer->transport->serial); + } else { + s->close(s); + } } } break; - case A_WRTE: - if (t->online) { - if((s = find_local_socket(p->msg.arg1))) { + case A_WRTE: /* WRITE(local-id, remote-id, <data>) */ + if (t->online && p->msg.arg0 != 0 && p->msg.arg1 != 0) { + if((s = find_local_socket(p->msg.arg1, p->msg.arg0))) { unsigned rid = p->msg.arg0; p->len = p->msg.data_length; @@ -1333,6 +1358,12 @@ int adb_main(int is_daemon, int server_port) D("Local port disabled\n"); } else { char local_name[30]; + if ((root_seclabel != NULL) && (is_selinux_enabled() > 0)) { + // b/12587913: fix setcon to allow const pointers + if (setcon((char *)root_seclabel) < 0) { + exit(1); + } + } build_local_name(local_name, sizeof(local_name), server_port); if(install_listener(local_name, "*smartsocket*", NULL, 0)) { exit(1); @@ -1619,10 +1650,6 @@ int handle_host_request(char *service, transport_type ttype, char* serial, int r return -1; } -#if !ADB_HOST -int recovery_mode = 0; -#endif - int main(int argc, char **argv) { #if ADB_HOST @@ -1634,9 +1661,26 @@ int main(int argc, char **argv) /* If adbd runs inside the emulator this will enable adb tracing via * adb-debug qemud service in the emulator. */ adb_qemu_trace_init(); - if((argc > 1) && (!strcmp(argv[1],"recovery"))) { - adb_device_banner = "recovery"; - recovery_mode = 1; + while(1) { + int c; + int option_index = 0; + static struct option opts[] = { + {"root_seclabel", required_argument, 0, 's' }, + {"device_banner", required_argument, 0, 'b' } + }; + c = getopt_long(argc, argv, "", opts, &option_index); + if (c == -1) + break; + switch (c) { + case 's': + root_seclabel = optarg; + break; + case 'b': + adb_device_banner = optarg; + break; + default: + break; + } } start_device_log(); @@ -122,6 +122,12 @@ struct asocket { */ void (*ready)(asocket *s); + /* shutdown is called by the peer before it goes away. + ** the socket should not do any further calls on its peer. + ** Always followed by a call to close. Optional, i.e. can be NULL. + */ + void (*shutdown)(asocket *s); + /* close is called by the peer when it has gone away. ** we are not allowed to make any further calls on the ** peer once our close method is called. @@ -233,7 +239,7 @@ struct alistener void print_packet(const char *label, apacket *p); -asocket *find_local_socket(unsigned id); +asocket *find_local_socket(unsigned local_id, unsigned remote_id); void install_local_socket(asocket *s); void remove_socket(asocket *s); void close_all_sockets(atransport *t); @@ -324,9 +330,7 @@ typedef enum { } BackupOperation; int backup_service(BackupOperation operation, char* args); void framebuffer_service(int fd, void *cookie); -void log_service(int fd, void *cookie); void remount_service(int fd, void *cookie); -char * get_log_file_path(const char * log_name); #endif /* packet allocator */ diff --git a/adb/adb_client.c b/adb/adb_client.c index af87d2a..586cd7b 100644 --- a/adb/adb_client.c +++ b/adb/adb_client.c @@ -278,7 +278,9 @@ int adb_connect(const char *service) return 0; fd = _adb_connect(service); - if(fd == -2) { + if(fd == -1) { + fprintf(stderr,"error: %s\n", __adb_error); + } else if(fd == -2) { fprintf(stderr,"** daemon still not running\n"); } D("adb_connect: return fd %d\n", fd); diff --git a/adb/commandline.c b/adb/commandline.c index 27a1754..61167b2 100644 --- a/adb/commandline.c +++ b/adb/commandline.c @@ -133,10 +133,11 @@ void help() " 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" + " adb install [-l] [-r] [-d] [-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" " ('-l' means forward-lock the app)\n" " ('-r' means reinstall the app, keeping its data)\n" + " ('-d' means allow version code downgrade)\n" " ('-s' means install on SD card instead of internal storage)\n" " ('--algo', '--key', and '--iv' mean the file is encrypted already)\n" " adb uninstall [-k] <package> - remove this app package from the device\n" @@ -767,7 +768,7 @@ static int restore(int argc, char** argv) { fd = adb_connect("restore:"); if (fd < 0) { - fprintf(stderr, "adb: unable to connect for backup\n"); + fprintf(stderr, "adb: unable to connect for restore\n"); adb_close(tarFd); return -1; } diff --git a/adb/file_sync_client.c b/adb/file_sync_client.c index 354d0fb..9fec081 100644 --- a/adb/file_sync_client.c +++ b/adb/file_sync_client.c @@ -642,8 +642,8 @@ static int local_build_list(copyinfo **filelist, ci = mkcopyinfo(lpath, rpath, name, 0); if(lstat(ci->src, &st)) { fprintf(stderr,"cannot stat '%s': %s\n", ci->src, strerror(errno)); + free(ci); closedir(d); - return -1; } if(!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) { diff --git a/adb/file_sync_service.c b/adb/file_sync_service.c index d3e841b..577fb8f 100644 --- a/adb/file_sync_service.c +++ b/adb/file_sync_service.c @@ -22,19 +22,31 @@ #include <sys/types.h> #include <dirent.h> #include <utime.h> +#include <unistd.h> #include <errno.h> - +#include <private/android_filesystem_config.h> +#include <selinux/android.h> #include "sysdeps.h" #define TRACE_TAG TRACE_SYNC #include "adb.h" #include "file_sync_service.h" +/* TODO: use fs_config to configure permissions on /data */ +static bool is_on_system(const char *name) { + const char *SYSTEM = "/system/"; + return (strncmp(SYSTEM, name, strlen(SYSTEM)) == 0); +} + static int mkdirs(char *name) { int ret; char *x = name + 1; + uid_t uid = -1; + gid_t gid = -1; + unsigned int mode = 0775; + uint64_t cap = 0; if(name[0] != '/') return -1; @@ -42,11 +54,21 @@ static int mkdirs(char *name) x = adb_dirstart(x); if(x == 0) return 0; *x = 0; - ret = adb_mkdir(name, 0775); + if (is_on_system(name)) { + fs_config(name, 1, &uid, &gid, &mode, &cap); + } + ret = adb_mkdir(name, mode); if((ret < 0) && (errno != EEXIST)) { D("mkdir(\"%s\") -> %s\n", name, strerror(errno)); *x = '/'; return ret; + } else if(ret == 0) { + ret = chown(name, uid, gid); + if (ret < 0) { + *x = '/'; + return ret; + } + selinux_android_restorecon(name); } *x++ = '/'; } @@ -110,6 +132,7 @@ static int do_list(int s, const char *path) if(writex(s, &msg.dent, sizeof(msg.dent)) || writex(s, de->d_name, len)) { + closedir(d); return -1; } } @@ -148,7 +171,8 @@ static int fail_errno(int s) return fail_message(s, strerror(errno)); } -static int handle_send_file(int s, char *path, mode_t mode, char *buffer) +static int handle_send_file(int s, char *path, uid_t uid, + gid_t gid, mode_t mode, char *buffer) { syncmsg msg; unsigned int timestamp = 0; @@ -156,8 +180,13 @@ static int handle_send_file(int s, char *path, mode_t mode, char *buffer) fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL, mode); if(fd < 0 && errno == ENOENT) { - mkdirs(path); - fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL, mode); + if(mkdirs(path) != 0) { + if(fail_errno(s)) + return -1; + fd = -1; + } else { + fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL, mode); + } } if(fd < 0 && errno == EEXIST) { fd = adb_open_mode(path, O_WRONLY, mode); @@ -166,6 +195,18 @@ static int handle_send_file(int s, char *path, mode_t mode, char *buffer) if(fail_errno(s)) return -1; fd = -1; + } else { + if(fchown(fd, uid, gid) != 0) { + fail_errno(s); + errno = 0; + } + + /* + * fchown clears the setuid bit - restore it if present. + * Ignore the result of calling fchmod. It's not supported + * by all filesystems. b/12441485 + */ + fchmod(fd, mode); } for(;;) { @@ -205,6 +246,7 @@ static int handle_send_file(int s, char *path, mode_t mode, char *buffer) if(fd >= 0) { struct utimbuf u; adb_close(fd); + selinux_android_restorecon(path); u.actime = timestamp; u.modtime = timestamp; utime(path, &u); @@ -248,7 +290,10 @@ static int handle_send_link(int s, char *path, char *buffer) ret = symlink(buffer, path); if(ret && errno == ENOENT) { - mkdirs(path); + if(mkdirs(path) != 0) { + fail_errno(s); + return -1; + } ret = symlink(buffer, path); } if(ret) { @@ -276,7 +321,7 @@ static int handle_send_link(int s, char *path, char *buffer) static int do_send(int s, char *path, char *buffer) { char *tmp; - mode_t mode; + unsigned int mode; int is_link, ret; tmp = strrchr(path,','); @@ -287,7 +332,7 @@ static int do_send(int s, char *path, char *buffer) #ifndef HAVE_SYMLINKS is_link = 0; #else - is_link = S_ISLNK(mode); + is_link = S_ISLNK((mode_t) mode); #endif mode &= 0777; } @@ -306,11 +351,22 @@ static int do_send(int s, char *path, char *buffer) #else { #endif + uid_t uid = -1; + gid_t gid = -1; + uint64_t cap = 0; + /* copy user permission bits to "group" and "other" permissions */ mode |= ((mode >> 3) & 0070); mode |= ((mode >> 3) & 0007); - ret = handle_send_file(s, path, mode, buffer); + tmp = path; + if(*tmp == '/') { + tmp++; + } + if (is_on_system(path)) { + fs_config(tmp, 0, &uid, &gid, &mode, &cap); + } + ret = handle_send_file(s, path, uid, gid, mode, buffer); } return ret; diff --git a/adb/framebuffer_service.c b/adb/framebuffer_service.c index 20c08d2..fa7fd98 100644 --- a/adb/framebuffer_service.c +++ b/adb/framebuffer_service.c @@ -55,13 +55,13 @@ struct fbinfo { void framebuffer_service(int fd, void *cookie) { struct fbinfo fbinfo; - unsigned int i; + unsigned int i, bsize; char buf[640]; int fd_screencap; int w, h, f; int fds[2]; - if (pipe(fds) < 0) goto done; + if (pipe(fds) < 0) goto pipefail; pid_t pid = fork(); if (pid < 0) goto done; @@ -164,17 +164,19 @@ void framebuffer_service(int fd, void *cookie) if(writex(fd, &fbinfo, sizeof(fbinfo))) goto done; /* write data */ - for(i = 0; i < fbinfo.size; i += sizeof(buf)) { - if(readx(fd_screencap, buf, sizeof(buf))) goto done; - if(writex(fd, buf, sizeof(buf))) goto done; + for(i = 0; i < fbinfo.size; i += bsize) { + bsize = sizeof(buf); + if (i + bsize > fbinfo.size) + bsize = fbinfo.size - i; + if(readx(fd_screencap, buf, bsize)) goto done; + if(writex(fd, buf, bsize)) goto done; } - if(readx(fd_screencap, buf, fbinfo.size % sizeof(buf))) goto done; - if(writex(fd, buf, fbinfo.size % sizeof(buf))) goto done; done: TEMP_FAILURE_RETRY(waitpid(pid, NULL, 0)); close(fds[0]); close(fds[1]); +pipefail: close(fd); } diff --git a/adb/log_service.c b/adb/log_service.c deleted file mode 100644 index af24356..0000000 --- a/adb/log_service.c +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2007 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 <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <string.h> -#include <fcntl.h> -#include <errno.h> -#include <sys/socket.h> -#include <log/logger.h> -#include "sysdeps.h" -#include "adb.h" - -#define LOG_FILE_DIR "/dev/log/" - -void write_log_entry(int fd, struct logger_entry *buf); - -void log_service(int fd, void *cookie) -{ - /* get the name of the log filepath to read */ - char * log_filepath = cookie; - - /* open the log file. */ - int logfd = unix_open(log_filepath, O_RDONLY); - if (logfd < 0) { - goto done; - } - - // temp buffer to read the entries - unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4))); - struct logger_entry *entry = (struct logger_entry *) buf; - - while (1) { - int ret; - - ret = unix_read(logfd, entry, LOGGER_ENTRY_MAX_LEN); - if (ret < 0) { - if (errno == EINTR || errno == EAGAIN) - continue; - // perror("logcat read"); - goto done; - } - else if (!ret) { - // fprintf(stderr, "read: Unexpected EOF!\n"); - goto done; - } - - /* NOTE: driver guarantees we read exactly one full entry */ - - entry->msg[entry->len] = '\0'; - - write_log_entry(fd, entry); - } - -done: - unix_close(fd); - free(log_filepath); -} - -/* returns the full path to the log file in a newly allocated string */ -char * get_log_file_path(const char * log_name) { - char *log_device = malloc(strlen(LOG_FILE_DIR) + strlen(log_name) + 1); - - strcpy(log_device, LOG_FILE_DIR); - strcat(log_device, log_name); - - return log_device; -} - - -/* prints one log entry into the file descriptor fd */ -void write_log_entry(int fd, struct logger_entry *buf) -{ - size_t size = sizeof(struct logger_entry) + buf->len; - - writex(fd, buf, size); -} diff --git a/adb/services.c b/adb/services.c index 89e595c..dcaf276 100644 --- a/adb/services.c +++ b/adb/services.c @@ -86,7 +86,7 @@ void restart_tcp_service(int fd, void *cookie) { char buf[100]; char value[PROPERTY_VALUE_MAX]; - int port = (int)cookie; + int port = (int) (uintptr_t) cookie; if (port <= 0) { snprintf(buf, sizeof(buf), "invalid port\n"); @@ -256,7 +256,7 @@ static int create_subprocess(const char *cmd, const char *arg0, const char *arg1 #if !ADB_HOST static void subproc_waiter_service(int fd, void *cookie) { - pid_t pid = (pid_t)cookie; + pid_t pid = (pid_t) (uintptr_t) cookie; D("entered. fd=%d of pid=%d\n", fd, pid); for (;;) { @@ -301,7 +301,7 @@ static int create_subproc_thread(const char *name) sti = malloc(sizeof(stinfo)); if(sti == 0) fatal("cannot allocate stinfo"); sti->func = subproc_waiter_service; - sti->cookie = (void*)pid; + sti->cookie = (void*) (uintptr_t) pid; sti->fd = ret_fd; if(adb_thread_create( &t, service_bootstrap_func, sti)){ @@ -355,8 +355,6 @@ int service_to_fd(const char *name) ret = create_service_thread(framebuffer_service, 0); } else if (!strncmp(name, "jdwp:", 5)) { ret = create_jdwp_connection_fd(atoi(name+5)); - } else if (!strncmp(name, "log:", 4)) { - ret = create_service_thread(log_service, get_log_file_path(name + 4)); } else if(!HOST && !strncmp(name, "shell:", 6)) { if(name[6]) { ret = create_subproc_thread(name + 6); @@ -384,7 +382,7 @@ int service_to_fd(const char *name) if (sscanf(name + 6, "%d", &port) == 0) { port = 0; } - ret = create_service_thread(restart_tcp_service, (void *)port); + ret = create_service_thread(restart_tcp_service, (void *) (uintptr_t) port); } else if(!strncmp(name, "usb:", 4)) { ret = create_service_thread(restart_usb_service, NULL); #endif diff --git a/adb/sockets.c b/adb/sockets.c index f17608b..de14a22 100644 --- a/adb/sockets.c +++ b/adb/sockets.c @@ -59,17 +59,22 @@ static asocket local_socket_closing_list = { .prev = &local_socket_closing_list, }; -asocket *find_local_socket(unsigned id) +// Parse the global list of sockets to find one with id |local_id|. +// If |peer_id| is not 0, also check that it is connected to a peer +// with id |peer_id|. Returns an asocket handle on success, NULL on failure. +asocket *find_local_socket(unsigned local_id, unsigned peer_id) { asocket *s; asocket *result = NULL; adb_mutex_lock(&socket_list_lock); for (s = local_socket_list.next; s != &local_socket_list; s = s->next) { - if (s->id == id) { + if (s->id != local_id) + continue; + if (peer_id == 0 || (s->peer && s->peer->id == peer_id)) { result = s; - break; } + break; } adb_mutex_unlock(&socket_list_lock); @@ -91,6 +96,11 @@ void install_local_socket(asocket *s) adb_mutex_lock(&socket_list_lock); s->id = local_socket_next_id++; + + // Socket ids should never be 0. + if (local_socket_next_id == 0) + local_socket_next_id = 1; + insert_local_socket(s, &local_socket_list); adb_mutex_unlock(&socket_list_lock); @@ -230,6 +240,12 @@ static void local_socket_close_locked(asocket *s) if(s->peer) { D("LS(%d): closing peer. peer->id=%d peer->fd=%d\n", s->id, s->peer->id, s->peer->fd); + /* Note: it's important to call shutdown before disconnecting from + * the peer, this ensures that remote sockets can still get the id + * of the local socket they're connected to, to send a CLOSE() + * protocol event. */ + if (s->peer->shutdown) + s->peer->shutdown(s->peer); s->peer->peer = 0; // tweak to avoid deadlock if (s->peer->close == local_socket_close) { @@ -326,7 +342,7 @@ static void local_socket_event_func(int fd, unsigned ev, void *_s) while(avail > 0) { r = adb_read(fd, x, avail); - D("LS(%d): post adb_read(fd=%d,...) r=%d (errno=%d) avail=%d\n", s->id, s->fd, r, r<0?errno:0, avail); + D("LS(%d): post adb_read(fd=%d,...) r=%d (errno=%d) avail=%zu\n", s->id, s->fd, r, r<0?errno:0, avail); if(r > 0) { avail -= r; x += r; @@ -397,6 +413,7 @@ asocket *create_local_socket(int fd) s->fd = fd; s->enqueue = local_socket_enqueue; s->ready = local_socket_ready; + s->shutdown = NULL; s->close = local_socket_close; install_local_socket(s); @@ -485,21 +502,29 @@ static void remote_socket_ready(asocket *s) send_packet(p, s->transport); } -static void remote_socket_close(asocket *s) +static void remote_socket_shutdown(asocket *s) { - D("entered remote_socket_close RS(%d) CLOSE fd=%d peer->fd=%d\n", + D("entered remote_socket_shutdown RS(%d) CLOSE fd=%d peer->fd=%d\n", s->id, s->fd, s->peer?s->peer->fd:-1); apacket *p = get_apacket(); p->msg.command = A_CLSE; if(s->peer) { p->msg.arg0 = s->peer->id; + } + p->msg.arg1 = s->id; + send_packet(p, s->transport); +} + +static void remote_socket_close(asocket *s) +{ + if (s->peer) { s->peer->peer = 0; D("RS(%d) peer->close()ing peer->id=%d peer->fd=%d\n", s->id, s->peer->id, s->peer->fd); s->peer->close(s->peer); } - p->msg.arg1 = s->id; - send_packet(p, s->transport); + D("entered remote_socket_close RS(%d) CLOSE fd=%d peer->fd=%d\n", + s->id, s->fd, s->peer?s->peer->fd:-1); D("RS(%d): closed\n", s->id); remove_transport_disconnect( s->transport, &((aremotesocket*)s)->disconnect ); free(s); @@ -519,15 +544,24 @@ static void remote_socket_disconnect(void* _s, atransport* t) free(s); } +/* Create an asocket to exchange packets with a remote service through transport + |t|. Where |id| is the socket id of the corresponding service on the other + side of the transport (it is allocated by the remote side and _cannot_ be 0). + Returns a new non-NULL asocket handle. */ asocket *create_remote_socket(unsigned id, atransport *t) { - asocket *s = calloc(1, sizeof(aremotesocket)); - adisconnect* dis = &((aremotesocket*)s)->disconnect; + asocket* s; + adisconnect* dis; + + if (id == 0) fatal("invalid remote socket id (0)"); + s = calloc(1, sizeof(aremotesocket)); + dis = &((aremotesocket*)s)->disconnect; if (s == NULL) fatal("cannot allocate socket"); s->id = id; s->enqueue = remote_socket_enqueue; s->ready = remote_socket_ready; + s->shutdown = remote_socket_shutdown; s->close = remote_socket_close; s->transport = t; @@ -562,6 +596,7 @@ void connect_to_remote(asocket *s, const char *destination) static void local_socket_ready_notify(asocket *s) { s->ready = local_socket_ready; + s->shutdown = NULL; s->close = local_socket_close; adb_write(s->fd, "OKAY", 4); s->ready(s); @@ -573,6 +608,7 @@ static void local_socket_ready_notify(asocket *s) static void local_socket_close_notify(asocket *s) { s->ready = local_socket_ready; + s->shutdown = NULL; s->close = local_socket_close; sendfailmsg(s->fd, "closed"); s->close(s); @@ -767,6 +803,7 @@ static int smart_socket_enqueue(asocket *s, apacket *p) adb_write(s->peer->fd, "OKAY", 4); s->peer->ready = local_socket_ready; + s->peer->shutdown = NULL; s->peer->close = local_socket_close; s->peer->peer = s2; s2->peer = s->peer; @@ -806,6 +843,7 @@ static int smart_socket_enqueue(asocket *s, apacket *p) ** tear down */ s->peer->ready = local_socket_ready_notify; + s->peer->shutdown = NULL; s->peer->close = local_socket_close_notify; s->peer->peer = 0; /* give him our transport and upref it */ @@ -851,6 +889,7 @@ static asocket *create_smart_socket(void) if (s == NULL) fatal("cannot allocate socket"); s->enqueue = smart_socket_enqueue; s->ready = smart_socket_ready; + s->shutdown = NULL; s->close = smart_socket_close; D("SS(%d)\n", s->id); diff --git a/adb/transport.c b/adb/transport.c index 224fe55..6002530 100644 --- a/adb/transport.c +++ b/adb/transport.c @@ -1142,9 +1142,9 @@ int readx(int fd, void *ptr, size_t len) char *p = ptr; int r; #if ADB_TRACE - int len0 = len; + size_t len0 = len; #endif - D("readx: fd=%d wanted=%d\n", fd, (int)len); + D("readx: fd=%d wanted=%zu\n", fd, len); while(len > 0) { r = adb_read(fd, p, len); if(r > 0) { @@ -1163,7 +1163,7 @@ int readx(int fd, void *ptr, size_t len) } #if ADB_TRACE - D("readx: fd=%d wanted=%d got=%d\n", fd, len0, len0 - len); + D("readx: fd=%d wanted=%zu got=%zu\n", fd, len0, len0 - len); dump_hex( ptr, len0 ); #endif return 0; diff --git a/adb/transport_local.c b/adb/transport_local.c index 1cfa24d..d2dbca6 100644 --- a/adb/transport_local.c +++ b/adb/transport_local.c @@ -159,7 +159,7 @@ static void *server_socket_thread(void * arg) int serverfd, fd; struct sockaddr addr; socklen_t alen; - int port = (int)arg; + int port = (int) (uintptr_t) arg; D("transport: server_socket_thread() starting\n"); serverfd = -1; @@ -241,7 +241,7 @@ static const char _start_req[] = "start"; /* 'ok' reply from the adb QEMUD service. */ static const char _ok_resp[] = "ok"; - const int port = (int)arg; + const int port = (int) (uintptr_t) arg; int res, fd; char tmp[256]; char con_name[32]; @@ -326,7 +326,7 @@ void local_init(int port) D("transport: local %s init\n", HOST ? "client" : "server"); - if(adb_thread_create(&thr, func, (void *)port)) { + if(adb_thread_create(&thr, func, (void *) (uintptr_t) port)) { fatal_errno("cannot create local socket %s thread", HOST ? "client" : "server"); } diff --git a/adb/usb_linux.c b/adb/usb_linux.c index 7bf2057..8ff753e 100644 --- a/adb/usb_linux.c +++ b/adb/usb_linux.c @@ -179,7 +179,7 @@ static void find_usb_device(const char *base, // should have device and configuration descriptors, and atleast two endpoints if (desclength < USB_DT_DEVICE_SIZE + USB_DT_CONFIG_SIZE) { - D("desclength %d is too small\n", desclength); + D("desclength %zu is too small\n", desclength); adb_close(fd); continue; } diff --git a/adb/usb_linux_client.c b/adb/usb_linux_client.c index fb1dad0..8426e0e 100644 --- a/adb/usb_linux_client.c +++ b/adb/usb_linux_client.c @@ -264,23 +264,25 @@ static void init_functionfs(struct usb_handle *h) { 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; - } + if (h->control < 0) { // might have already done this before + 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, &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; + 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); @@ -320,14 +322,14 @@ static void *usb_ffs_open_thread(void *x) while (1) { // wait until the USB device needs opening adb_mutex_lock(&usb->lock); - while (usb->control != -1) + while (usb->control != -1 && usb->bulk_in != -1 && usb->bulk_out != -1) adb_cond_wait(&usb->notify, &usb->lock); adb_mutex_unlock(&usb->lock); while (1) { init_functionfs(usb); - if (usb->control >= 0) + if (usb->control >= 0 && usb->bulk_in >= 0 && usb->bulk_out >= 0) break; adb_sleep_ms(1000); @@ -384,7 +386,7 @@ static int bulk_read(int bulk_out, char *buf, size_t length) 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", + D("[ bulk_read failed fd=%d length=%zu count=%zu ]\n", bulk_out, length, count); return ret; } @@ -424,10 +426,13 @@ static void usb_ffs_kick(usb_handle *h) D("[ kick: sink (fd=%d) clear halt failed (%d) ]", h->bulk_out, errno); adb_mutex_lock(&h->lock); - adb_close(h->control); + + // don't close ep0 here, since we may not need to reinitialize it with + // the same descriptors again. if however ep1/ep2 fail to re-open in + // init_functionfs, only then would we close and open ep0 again. adb_close(h->bulk_out); adb_close(h->bulk_in); - h->control = h->bulk_out = h->bulk_in = -1; + h->bulk_out = h->bulk_in = -1; // notify usb_ffs_open_thread that we are disconnected adb_cond_signal(&h->notify); diff --git a/adb/usb_vendors.c b/adb/usb_vendors.c index 68bb232..cd5885e 100644..100755 --- a/adb/usb_vendors.c +++ b/adb/usb_vendors.c @@ -35,191 +35,227 @@ #define TRACE_TAG TRACE_USB -// Google's USB Vendor ID -#define VENDOR_ID_GOOGLE 0x18d1 -// Intel's USB Vendor ID -#define VENDOR_ID_INTEL 0x8087 -// HTC's USB Vendor ID -#define VENDOR_ID_HTC 0x0bb4 -// Samsung's USB Vendor ID -#define VENDOR_ID_SAMSUNG 0x04e8 -// Motorola's USB Vendor ID -#define VENDOR_ID_MOTOROLA 0x22b8 -// LG's USB Vendor ID -#define VENDOR_ID_LGE 0x1004 -// Huawei's USB Vendor ID -#define VENDOR_ID_HUAWEI 0x12D1 +/* Keep the list below sorted alphabetically by #define name */ // Acer's USB Vendor ID #define VENDOR_ID_ACER 0x0502 -// Sony Ericsson's USB Vendor ID -#define VENDOR_ID_SONY_ERICSSON 0x0FCE -// Foxconn's USB Vendor ID -#define VENDOR_ID_FOXCONN 0x0489 -// Dell's USB Vendor ID -#define VENDOR_ID_DELL 0x413c -// Nvidia's USB Vendor ID -#define VENDOR_ID_NVIDIA 0x0955 -// Garmin-Asus's USB Vendor ID -#define VENDOR_ID_GARMIN_ASUS 0x091E -// Sharp's USB Vendor ID -#define VENDOR_ID_SHARP 0x04dd -// ZTE's USB Vendor ID -#define VENDOR_ID_ZTE 0x19D2 -// Kyocera's USB Vendor ID -#define VENDOR_ID_KYOCERA 0x0482 -// Pantech's USB Vendor ID -#define VENDOR_ID_PANTECH 0x10A9 -// Qualcomm's USB Vendor ID -#define VENDOR_ID_QUALCOMM 0x05c6 -// On-The-Go-Video's USB Vendor ID -#define VENDOR_ID_OTGV 0x2257 -// NEC's USB Vendor ID -#define VENDOR_ID_NEC 0x0409 -// Panasonic Mobile Communication's USB Vendor ID -#define VENDOR_ID_PMC 0x04DA -// Toshiba's USB Vendor ID -#define VENDOR_ID_TOSHIBA 0x0930 -// SK Telesys's USB Vendor ID -#define VENDOR_ID_SK_TELESYS 0x1F53 -// KT Tech's USB Vendor ID -#define VENDOR_ID_KT_TECH 0x2116 +// Allwinner's USB Vendor ID +#define VENDOR_ID_ALLWINNER 0x1F3A +// Amlogic's USB Vendor ID +#define VENDOR_ID_AMLOGIC 0x1b8e +// AnyDATA's USB Vendor ID +#define VENDOR_ID_ANYDATA 0x16D5 +// Archos's USB Vendor ID +#define VENDOR_ID_ARCHOS 0x0E79 // Asus's USB Vendor ID #define VENDOR_ID_ASUS 0x0b05 -// Philips's USB Vendor ID -#define VENDOR_ID_PHILIPS 0x0471 -// Texas Instruments's USB Vendor ID -#define VENDOR_ID_TI 0x0451 +// BYD's USB Vendor ID +#define VENDOR_ID_BYD 0x1D91 +// Compal's USB Vendor ID +#define VENDOR_ID_COMPAL 0x1219 +// Dell's USB Vendor ID +#define VENDOR_ID_DELL 0x413c +// ECS's USB Vendor ID +#define VENDOR_ID_ECS 0x03fc +// EMERGING_TECH's USB Vendor ID +#define VENDOR_ID_EMERGING_TECH 0x297F +// Emerson's USB Vendor ID +#define VENDOR_ID_EMERSON 0x2207 +// Foxconn's USB Vendor ID +#define VENDOR_ID_FOXCONN 0x0489 +// Fujitsu's USB Vendor ID +#define VENDOR_ID_FUJITSU 0x04C5 // Funai's USB Vendor ID #define VENDOR_ID_FUNAI 0x0F1C +// Garmin-Asus's USB Vendor ID +#define VENDOR_ID_GARMIN_ASUS 0x091E // Gigabyte's USB Vendor ID #define VENDOR_ID_GIGABYTE 0x0414 +// Gigaset's USB Vendor ID +#define VENDOR_ID_GIGASET 0x1E85 +// Google's USB Vendor ID +#define VENDOR_ID_GOOGLE 0x18d1 +// Haier's USB Vendor ID +#define VENDOR_ID_HAIER 0x201E +// Harris's USB Vendor ID +#define VENDOR_ID_HARRIS 0x19A5 +// Hisense's USB Vendor ID +#define VENDOR_ID_HISENSE 0x109b +// HP's USB Vendor ID +#define VENDOR_ID_HP 0x03f0 +// HTC's USB Vendor ID +#define VENDOR_ID_HTC 0x0bb4 +// Huawei's USB Vendor ID +#define VENDOR_ID_HUAWEI 0x12D1 +// INQ Mobile's USB Vendor ID +#define VENDOR_ID_INQ_MOBILE 0x2314 +// Intel's USB Vendor ID +#define VENDOR_ID_INTEL 0x8087 // IRiver's USB Vendor ID #define VENDOR_ID_IRIVER 0x2420 -// Compal's USB Vendor ID -#define VENDOR_ID_COMPAL 0x1219 -// T & A Mobile Phones' USB Vendor ID -#define VENDOR_ID_T_AND_A 0x1BBB -// LenovoMobile's USB Vendor ID -#define VENDOR_ID_LENOVOMOBILE 0x2006 -// Lenovo's USB Vendor ID -#define VENDOR_ID_LENOVO 0x17EF -// Vizio's USB Vendor ID -#define VENDOR_ID_VIZIO 0xE040 // K-Touch's USB Vendor ID #define VENDOR_ID_K_TOUCH 0x24E3 +// KT Tech's USB Vendor ID +#define VENDOR_ID_KT_TECH 0x2116 +// Kobo's USB Vendor ID +#define VENDOR_ID_KOBO 0x2237 +// Kyocera's USB Vendor ID +#define VENDOR_ID_KYOCERA 0x0482 +// Lab126's USB Vendor ID +#define VENDOR_ID_LAB126 0x1949 +// Lenovo's USB Vendor ID +#define VENDOR_ID_LENOVO 0x17EF +// LenovoMobile's USB Vendor ID +#define VENDOR_ID_LENOVOMOBILE 0x2006 +// LG's USB Vendor ID +#define VENDOR_ID_LGE 0x1004 +// Lumigon's USB Vendor ID +#define VENDOR_ID_LUMIGON 0x25E3 +// Motorola's USB Vendor ID +#define VENDOR_ID_MOTOROLA 0x22b8 +// MSI's USB Vendor ID +#define VENDOR_ID_MSI 0x0DB0 +// MTK's USB Vendor ID +#define VENDOR_ID_MTK 0x0e8d +// NEC's USB Vendor ID +#define VENDOR_ID_NEC 0x0409 +// B&N Nook's USB Vendor ID +#define VENDOR_ID_NOOK 0x2080 +// Nvidia's USB Vendor ID +#define VENDOR_ID_NVIDIA 0x0955 +// OPPO's USB Vendor ID +#define VENDOR_ID_OPPO 0x22D9 +// On-The-Go-Video's USB Vendor ID +#define VENDOR_ID_OTGV 0x2257 +// OUYA's USB Vendor ID +#define VENDOR_ID_OUYA 0x2836 +// Pantech's USB Vendor ID +#define VENDOR_ID_PANTECH 0x10A9 // Pegatron's USB Vendor ID #define VENDOR_ID_PEGATRON 0x1D4D -// Archos's USB Vendor ID -#define VENDOR_ID_ARCHOS 0x0E79 +// Philips's USB Vendor ID +#define VENDOR_ID_PHILIPS 0x0471 +// Panasonic Mobile Communication's USB Vendor ID +#define VENDOR_ID_PMC 0x04DA // Positivo's USB Vendor ID #define VENDOR_ID_POSITIVO 0x1662 -// Fujitsu's USB Vendor ID -#define VENDOR_ID_FUJITSU 0x04C5 -// Lumigon's USB Vendor ID -#define VENDOR_ID_LUMIGON 0x25E3 +// Qisda's USB Vendor ID +#define VENDOR_ID_QISDA 0x1D45 +// Qualcomm's USB Vendor ID +#define VENDOR_ID_QUALCOMM 0x05c6 // Quanta's USB Vendor ID #define VENDOR_ID_QUANTA 0x0408 -// INQ Mobile's USB Vendor ID -#define VENDOR_ID_INQ_MOBILE 0x2314 +// Rockchip's USB Vendor ID +#define VENDOR_ID_ROCKCHIP 0x2207 +// Samsung's USB Vendor ID +#define VENDOR_ID_SAMSUNG 0x04e8 +// Sharp's USB Vendor ID +#define VENDOR_ID_SHARP 0x04dd +// SK Telesys's USB Vendor ID +#define VENDOR_ID_SK_TELESYS 0x1F53 // Sony's USB Vendor ID #define VENDOR_ID_SONY 0x054C -// Lab126's USB Vendor ID -#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 +// Sony Ericsson's USB Vendor ID +#define VENDOR_ID_SONY_ERICSSON 0x0FCE +// T & A Mobile Phones' USB Vendor ID +#define VENDOR_ID_T_AND_A 0x1BBB +// TechFaith's USB Vendor ID +#define VENDOR_ID_TECHFAITH 0x1d09 // Teleepoch's USB Vendor ID #define VENDOR_ID_TELEEPOCH 0x2340 -// AnyDATA's USB Vendor ID -#define VENDOR_ID_ANYDATA 0x16D5 -// Harris's USB Vendor ID -#define VENDOR_ID_HARRIS 0x19A5 -// OPPO's USB Vendor ID -#define VENDOR_ID_OPPO 0x22D9 +// Texas Instruments's USB Vendor ID +#define VENDOR_ID_TI 0x0451 +// Toshiba's USB Vendor ID +#define VENDOR_ID_TOSHIBA 0x0930 +// Vizio's USB Vendor ID +#define VENDOR_ID_VIZIO 0xE040 +// Wacom's USB Vendor ID +#define VENDOR_ID_WACOM 0x0531 // Xiaomi's USB Vendor ID #define VENDOR_ID_XIAOMI 0x2717 -// BYD's USB Vendor ID -#define VENDOR_ID_BYD 0x19D1 -// OUYA's USB Vendor ID -#define VENDOR_ID_OUYA 0x2836 -// Haier's USB Vendor ID -#define VENDOR_ID_HAIER 0x201E -// Hisense's USB Vendor ID -#define VENDOR_ID_HISENSE 0x109b -// MTK's USB Vendor ID -#define VENDOR_ID_MTK 0x0e8d -// B&N Nook's USB Vendor ID -#define VENDOR_ID_NOOK 0x2080 -// Qisda's USB Vendor ID -#define VENDOR_ID_QISDA 0x1D45 -// ECS's USB Vendor ID -#define VENDOR_ID_ECS 0x03fc - +// YotaDevices's USB Vendor ID +#define VENDOR_ID_YOTADEVICES 0x2916 +// Yulong Coolpad's USB Vendor ID +#define VENDOR_ID_YULONG_COOLPAD 0x1EBF +// ZTE's USB Vendor ID +#define VENDOR_ID_ZTE 0x19D2 +/* Keep the list above sorted alphabetically by #define name */ /** built-in vendor list */ +/* Keep the list below sorted alphabetically */ int builtInVendorIds[] = { - VENDOR_ID_GOOGLE, - VENDOR_ID_INTEL, - VENDOR_ID_HTC, - VENDOR_ID_SAMSUNG, - VENDOR_ID_MOTOROLA, - VENDOR_ID_LGE, - VENDOR_ID_HUAWEI, VENDOR_ID_ACER, - VENDOR_ID_SONY_ERICSSON, - VENDOR_ID_FOXCONN, - VENDOR_ID_DELL, - VENDOR_ID_NVIDIA, - VENDOR_ID_GARMIN_ASUS, - VENDOR_ID_SHARP, - VENDOR_ID_ZTE, - VENDOR_ID_KYOCERA, - VENDOR_ID_PANTECH, - VENDOR_ID_QUALCOMM, - VENDOR_ID_OTGV, - VENDOR_ID_NEC, - VENDOR_ID_PMC, - VENDOR_ID_TOSHIBA, - VENDOR_ID_SK_TELESYS, - VENDOR_ID_KT_TECH, + VENDOR_ID_ALLWINNER, + VENDOR_ID_AMLOGIC, + VENDOR_ID_ANYDATA, + VENDOR_ID_ARCHOS, VENDOR_ID_ASUS, - VENDOR_ID_PHILIPS, - VENDOR_ID_TI, + VENDOR_ID_BYD, + VENDOR_ID_COMPAL, + VENDOR_ID_DELL, + VENDOR_ID_ECS, + VENDOR_ID_EMERGING_TECH, + VENDOR_ID_EMERSON, + VENDOR_ID_FOXCONN, + VENDOR_ID_FUJITSU, VENDOR_ID_FUNAI, + VENDOR_ID_GARMIN_ASUS, VENDOR_ID_GIGABYTE, + VENDOR_ID_GIGASET, + VENDOR_ID_GOOGLE, + VENDOR_ID_HAIER, + VENDOR_ID_HARRIS, + VENDOR_ID_HISENSE, + VENDOR_ID_HP, + VENDOR_ID_HTC, + VENDOR_ID_HUAWEI, + VENDOR_ID_INQ_MOBILE, + VENDOR_ID_INTEL, VENDOR_ID_IRIVER, - VENDOR_ID_COMPAL, - VENDOR_ID_T_AND_A, - VENDOR_ID_LENOVOMOBILE, - VENDOR_ID_LENOVO, - VENDOR_ID_VIZIO, + VENDOR_ID_KOBO, VENDOR_ID_K_TOUCH, + VENDOR_ID_KT_TECH, + VENDOR_ID_KYOCERA, + VENDOR_ID_LAB126, + VENDOR_ID_LENOVO, + VENDOR_ID_LENOVOMOBILE, + VENDOR_ID_LGE, + VENDOR_ID_LUMIGON, + VENDOR_ID_MOTOROLA, + VENDOR_ID_MSI, + VENDOR_ID_MTK, + VENDOR_ID_NEC, + VENDOR_ID_NOOK, + VENDOR_ID_NVIDIA, + VENDOR_ID_OPPO, + VENDOR_ID_OTGV, + VENDOR_ID_OUYA, + VENDOR_ID_PANTECH, VENDOR_ID_PEGATRON, - VENDOR_ID_ARCHOS, + VENDOR_ID_PHILIPS, + VENDOR_ID_PMC, VENDOR_ID_POSITIVO, - VENDOR_ID_FUJITSU, - VENDOR_ID_LUMIGON, + VENDOR_ID_QISDA, + VENDOR_ID_QUALCOMM, VENDOR_ID_QUANTA, - VENDOR_ID_INQ_MOBILE, + VENDOR_ID_ROCKCHIP, + VENDOR_ID_SAMSUNG, + VENDOR_ID_SHARP, + VENDOR_ID_SK_TELESYS, VENDOR_ID_SONY, - VENDOR_ID_LAB126, - VENDOR_ID_YULONG_COOLPAD, - VENDOR_ID_KOBO, + VENDOR_ID_SONY_ERICSSON, + VENDOR_ID_T_AND_A, + VENDOR_ID_TECHFAITH, VENDOR_ID_TELEEPOCH, - VENDOR_ID_ANYDATA, - VENDOR_ID_HARRIS, - VENDOR_ID_OPPO, + VENDOR_ID_TI, + VENDOR_ID_TOSHIBA, + VENDOR_ID_VIZIO, + VENDOR_ID_WACOM, VENDOR_ID_XIAOMI, - VENDOR_ID_BYD, - VENDOR_ID_OUYA, - VENDOR_ID_HAIER, - VENDOR_ID_HISENSE, - VENDOR_ID_MTK, - VENDOR_ID_NOOK, - VENDOR_ID_QISDA, - VENDOR_ID_ECS, + VENDOR_ID_YOTADEVICES, + VENDOR_ID_YULONG_COOLPAD, + VENDOR_ID_ZTE, }; +/* Keep the list above sorted alphabetically */ #define BUILT_IN_VENDOR_COUNT (sizeof(builtInVendorIds)/sizeof(builtInVendorIds[0])) diff --git a/adf/Android.mk b/adf/Android.mk new file mode 100644 index 0000000..64d486e --- /dev/null +++ b/adf/Android.mk @@ -0,0 +1,18 @@ +# +# 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. +# +LOCAL_PATH := $(my-dir) + +include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/adf/libadf/Android.mk b/adf/libadf/Android.mk new file mode 100644 index 0000000..908aa6c --- /dev/null +++ b/adf/libadf/Android.mk @@ -0,0 +1,23 @@ +# 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. + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := adf.c +LOCAL_MODULE := libadf +LOCAL_MODULE_TAGS := optional +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS) +include $(BUILD_STATIC_LIBRARY) diff --git a/adf/libadf/adf.c b/adf/libadf/adf.c new file mode 100644 index 0000000..871629e --- /dev/null +++ b/adf/libadf/adf.c @@ -0,0 +1,810 @@ +/* + * 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. + */ + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include <linux/limits.h> + +#include <sys/ioctl.h> + +#include <adf/adf.h> + +#define ADF_BASE_PATH "/dev/" + +static ssize_t adf_find_nodes(const char *pattern, adf_id_t **ids) +{ + DIR *dir; + struct dirent *dirent; + size_t n = 0; + ssize_t ret; + adf_id_t *ids_ret = NULL; + + dir = opendir(ADF_BASE_PATH); + if (!dir) + return -errno; + + errno = 0; + while ((dirent = readdir(dir))) { + adf_id_t id; + int matched = sscanf(dirent->d_name, pattern, &id); + + if (matched < 0) { + ret = -errno; + goto done; + } else if (matched != 1) { + continue; + } + + adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0])); + if (!new_ids) { + ret = -ENOMEM; + goto done; + } + + ids_ret = new_ids; + ids_ret[n] = id; + n++; + } + if (errno) + ret = -errno; + else + ret = n; + +done: + closedir(dir); + if (ret < 0) + free(ids_ret); + else + *ids = ids_ret; + return ret; +} + +ssize_t adf_devices(adf_id_t **ids) +{ + return adf_find_nodes("adf%u", ids); +} + +int adf_device_open(adf_id_t id, int flags, struct adf_device *dev) +{ + char filename[64]; + int err; + + dev->id = id; + + snprintf(filename, sizeof(filename), ADF_BASE_PATH "adf%u", id); + dev->fd = open(filename, flags); + if (dev->fd < 0) + return -errno; + + return 0; +} + +void adf_device_close(struct adf_device *dev) +{ + if (dev->fd >= 0) + close(dev->fd); +} + +int adf_get_device_data(struct adf_device *dev, struct adf_device_data *data) +{ + int err; + int ret = 0; + + memset(data, 0, sizeof(*data)); + + err = ioctl(dev->fd, ADF_GET_DEVICE_DATA, data); + if (err < 0) + return -ENOMEM; + + if (data->n_attachments) { + data->attachments = malloc(sizeof(data->attachments[0]) * + data->n_attachments); + if (!data->attachments) + return -ENOMEM; + } + + if (data->n_allowed_attachments) { + data->allowed_attachments = + malloc(sizeof(data->allowed_attachments[0]) * + data->n_allowed_attachments); + if (!data->allowed_attachments) { + ret = -ENOMEM; + goto done; + } + } + + if (data->custom_data_size) { + data->custom_data = malloc(data->custom_data_size); + if (!data->custom_data) { + ret = -ENOMEM; + goto done; + } + } + + err = ioctl(dev->fd, ADF_GET_DEVICE_DATA, data); + if (err < 0) + ret = -errno; + +done: + if (ret < 0) + adf_free_device_data(data); + return ret; +} + +void adf_free_device_data(struct adf_device_data *data) +{ + free(data->attachments); + free(data->allowed_attachments); + free(data->custom_data); +} + +int adf_device_post(struct adf_device *dev, + adf_id_t *interfaces, size_t n_interfaces, + struct adf_buffer_config *bufs, size_t n_bufs, + void *custom_data, size_t custom_data_size) +{ + int err; + struct adf_post_config data; + + memset(&data, 0, sizeof(data)); + data.interfaces = interfaces; + data.n_interfaces = n_interfaces; + data.bufs = bufs; + data.n_bufs = n_bufs; + data.custom_data = custom_data; + data.custom_data_size = custom_data_size; + + err = ioctl(dev->fd, ADF_POST_CONFIG, &data); + if (err < 0) + return -errno; + + return (int)data.complete_fence; +} + +static int adf_device_attachment(struct adf_device *dev, + adf_id_t overlay_engine, adf_id_t interface, bool attach) +{ + int err; + struct adf_attachment_config data; + + memset(&data, 0, sizeof(data)); + data.overlay_engine = overlay_engine; + data.interface = interface; + + err = ioctl(dev->fd, attach ? ADF_ATTACH : ADF_DETACH, &data); + if (err < 0) + return -errno; + + return 0; +} + +int adf_device_attach(struct adf_device *dev, adf_id_t overlay_engine, + adf_id_t interface) +{ + return adf_device_attachment(dev, overlay_engine, interface, true); +} + +int adf_device_detach(struct adf_device *dev, adf_id_t overlay_engine, + adf_id_t interface) +{ + return adf_device_attachment(dev, overlay_engine, interface, false); +} + +ssize_t adf_interfaces(struct adf_device *dev, adf_id_t **interfaces) +{ + char pattern[64]; + + snprintf(pattern, sizeof(pattern), "adf-interface%u.%%u", dev->id); + return adf_find_nodes(pattern, interfaces); +} + +ssize_t adf_interfaces_for_overlay_engine(struct adf_device *dev, + adf_id_t overlay_engine, adf_id_t **interfaces) +{ + struct adf_device_data data; + ssize_t n = 0; + ssize_t ret; + adf_id_t *ids_ret = NULL; + + ret = adf_get_device_data(dev, &data); + if (ret < 0) + return ret; + + size_t i; + for (i = 0; i < data.n_allowed_attachments; i++) { + if (data.allowed_attachments[i].overlay_engine != overlay_engine) + continue; + + adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0])); + if (!new_ids) { + ret = -ENOMEM; + goto done; + } + + ids_ret = new_ids; + ids_ret[n] = data.allowed_attachments[i].interface; + n++; + } + + ret = n; + +done: + adf_free_device_data(&data); + if (ret < 0) + free(ids_ret); + else + *interfaces = ids_ret; + return ret; +} + +static ssize_t adf_interfaces_filter(struct adf_device *dev, + adf_id_t *in, size_t n_in, adf_id_t **out, + bool (*filter)(struct adf_interface_data *data, __u32 match), + __u32 match) +{ + size_t n = 0; + ssize_t ret; + adf_id_t *ids_ret = NULL; + + size_t i; + for (i = 0; i < n_in; i++) { + int fd = adf_interface_open(dev, in[i], O_RDONLY); + if (fd < 0) { + ret = fd; + goto done; + } + + struct adf_interface_data data; + ret = adf_get_interface_data(fd, &data); + close(fd); + if (ret < 0) + goto done; + + if (!filter(&data, match)) + continue; + + adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0])); + if (!new_ids) { + ret = -ENOMEM; + goto done; + } + + ids_ret = new_ids; + ids_ret[n] = in[i]; + n++; + } + + ret = n; + +done: + if (ret < 0) + free(ids_ret); + else + *out = ids_ret; + return ret; +} + +static bool adf_interface_type_filter(struct adf_interface_data *data, + __u32 type) +{ + return data->type == (enum adf_interface_type)type; +} + +ssize_t adf_interfaces_filter_by_type(struct adf_device *dev, + enum adf_interface_type type, + adf_id_t *in, size_t n_in, adf_id_t **out) +{ + return adf_interfaces_filter(dev, in, n_in, out, adf_interface_type_filter, + type); +} + +static bool adf_interface_flags_filter(struct adf_interface_data *data, + __u32 flag) +{ + return !!(data->flags & flag); +} + +ssize_t adf_interfaces_filter_by_flag(struct adf_device *dev, __u32 flag, + adf_id_t *in, size_t n_in, adf_id_t **out) +{ + return adf_interfaces_filter(dev, in, n_in, out, adf_interface_flags_filter, + flag); +} + +int adf_interface_open(struct adf_device *dev, adf_id_t id, int flags) +{ + char filename[64]; + + snprintf(filename, sizeof(filename), ADF_BASE_PATH "adf-interface%u.%u", + dev->id, id); + + int fd = open(filename, flags); + if (fd < 0) + return -errno; + return fd; +} + +int adf_get_interface_data(int fd, struct adf_interface_data *data) +{ + int err; + int ret = 0; + + memset(data, 0, sizeof(*data)); + + err = ioctl(fd, ADF_GET_INTERFACE_DATA, data); + if (err < 0) + return -errno; + + if (data->n_available_modes) { + data->available_modes = malloc(sizeof(data->available_modes[0]) * + data->n_available_modes); + if (!data->available_modes) + return -ENOMEM; + } + + if (data->custom_data_size) { + data->custom_data = malloc(data->custom_data_size); + if (!data->custom_data) { + ret = -ENOMEM; + goto done; + } + } + + err = ioctl(fd, ADF_GET_INTERFACE_DATA, data); + if (err < 0) + ret = -errno; + +done: + if (ret < 0) + adf_free_interface_data(data); + return ret; +} + +void adf_free_interface_data(struct adf_interface_data *data) +{ + free(data->available_modes); + free(data->custom_data); +} + +int adf_interface_blank(int fd, __u8 mode) +{ + int err = ioctl(fd, ADF_BLANK, mode); + if (err < 0) + return -errno; + return 0; +} + +int adf_interface_set_mode(int fd, struct drm_mode_modeinfo *mode) +{ + int err = ioctl(fd, ADF_SET_MODE, mode); + if (err < 0) + return -errno; + return 0; +} + +int adf_interface_simple_buffer_alloc(int fd, __u32 w, __u32 h, + __u32 format, __u32 *offset, __u32 *pitch) +{ + int err; + struct adf_simple_buffer_alloc data; + + memset(&data, 0, sizeof(data)); + data.w = w; + data.h = h; + data.format = format; + + err = ioctl(fd, ADF_SIMPLE_BUFFER_ALLOC, &data); + if (err < 0) + return -errno; + + *offset = data.offset; + *pitch = data.pitch; + return (int)data.fd; +} + +int adf_interface_simple_post(int fd, __u32 overlay_engine, + __u32 w, __u32 h, __u32 format, int buf_fd, __u32 offset, + __u32 pitch, int acquire_fence) +{ + int ret; + struct adf_simple_post_config data; + + memset(&data, 0, sizeof(data)); + data.buf.overlay_engine = overlay_engine; + data.buf.w = w; + data.buf.h = h; + data.buf.format = format; + data.buf.fd[0] = buf_fd; + data.buf.offset[0] = offset; + data.buf.pitch[0] = pitch; + data.buf.n_planes = 1; + data.buf.acquire_fence = acquire_fence; + + ret = ioctl(fd, ADF_SIMPLE_POST_CONFIG, &data); + if (ret < 0) + return -errno; + + return (int)data.complete_fence; +} + +ssize_t adf_overlay_engines(struct adf_device *dev, adf_id_t **overlay_engines) +{ + char pattern[64]; + + snprintf(pattern, sizeof(pattern), "adf-overlay-engine%u.%%u", dev->id); + return adf_find_nodes(pattern, overlay_engines); +} + +ssize_t adf_overlay_engines_for_interface(struct adf_device *dev, + adf_id_t interface, adf_id_t **overlay_engines) +{ + struct adf_device_data data; + ssize_t n = 0; + ssize_t ret; + adf_id_t *ids_ret = NULL; + + ret = adf_get_device_data(dev, &data); + if (ret < 0) + return ret; + + size_t i; + for (i = 0; i < data.n_allowed_attachments; i++) { + if (data.allowed_attachments[i].interface != interface) + continue; + + adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0])); + if (!new_ids) { + ret = -ENOMEM; + goto done; + } + + ids_ret = new_ids; + ids_ret[n] = data.allowed_attachments[i].overlay_engine; + n++; + } + + ret = n; + +done: + adf_free_device_data(&data); + if (ret < 0) + free(ids_ret); + else + *overlay_engines = ids_ret; + return ret; +} + +static ssize_t adf_overlay_engines_filter(struct adf_device *dev, + adf_id_t *in, size_t n_in, adf_id_t **out, + bool (*filter)(struct adf_overlay_engine_data *data, void *cookie), + void *cookie) +{ + size_t n = 0; + ssize_t ret; + adf_id_t *ids_ret = NULL; + + size_t i; + for (i = 0; i < n_in; i++) { + int fd = adf_overlay_engine_open(dev, in[i], O_RDONLY); + if (fd < 0) { + ret = fd; + goto done; + } + + struct adf_overlay_engine_data data; + ret = adf_get_overlay_engine_data(fd, &data); + close(fd); + if (ret < 0) + goto done; + + if (!filter(&data, cookie)) + continue; + + adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0])); + if (!new_ids) { + ret = -ENOMEM; + goto done; + } + + ids_ret = new_ids; + ids_ret[n] = in[i]; + n++; + } + + ret = n; + +done: + if (ret < 0) + free(ids_ret); + else + *out = ids_ret; + return ret; +} + +struct format_filter_cookie { + const __u32 *formats; + size_t n_formats; +}; + +static bool adf_overlay_engine_format_filter( + struct adf_overlay_engine_data *data, void *cookie) +{ + struct format_filter_cookie *c = cookie; + size_t i; + for (i = 0; i < data->n_supported_formats; i++) { + size_t j; + for (j = 0; j < c->n_formats; j++) + if (data->supported_formats[i] == c->formats[j]) + return true; + } + return false; +} + +ssize_t adf_overlay_engines_filter_by_format(struct adf_device *dev, + const __u32 *formats, size_t n_formats, adf_id_t *in, size_t n_in, + adf_id_t **out) +{ + struct format_filter_cookie cookie = { formats, n_formats }; + return adf_overlay_engines_filter(dev, in, n_in, out, + adf_overlay_engine_format_filter, &cookie); +} + +int adf_overlay_engine_open(struct adf_device *dev, adf_id_t id, int flags) +{ + char filename[64]; + + snprintf(filename, sizeof(filename), + ADF_BASE_PATH "adf-overlay-engine%u.%u", dev->id, id); + + int fd = open(filename, flags); + if (fd < 0) + return -errno; + return fd; +} + +int adf_get_overlay_engine_data(int fd, struct adf_overlay_engine_data *data) +{ + int err; + int ret = 0; + + memset(data, 0, sizeof(*data)); + + err = ioctl(fd, ADF_GET_OVERLAY_ENGINE_DATA, data); + if (err < 0) + return -errno; + + if (data->n_supported_formats) { + data->supported_formats = malloc(sizeof(data->supported_formats[0]) * + data->n_supported_formats); + if (!data->supported_formats) + return -ENOMEM; + } + + if (data->custom_data_size) { + data->custom_data = malloc(data->custom_data_size); + if (!data->custom_data) { + ret = -ENOMEM; + goto done; + } + } + + err = ioctl(fd, ADF_GET_OVERLAY_ENGINE_DATA, data); + if (err < 0) + ret = -errno; + +done: + if (ret < 0) + adf_free_overlay_engine_data(data); + return ret; +} + +void adf_free_overlay_engine_data(struct adf_overlay_engine_data *data) +{ + free(data->supported_formats); + free(data->custom_data); +} + +bool adf_overlay_engine_supports_format(int fd, __u32 format) +{ + struct adf_overlay_engine_data data; + bool ret = false; + size_t i; + + int err = adf_get_overlay_engine_data(fd, &data); + if (err < 0) + return false; + + for (i = 0; i < data.n_supported_formats; i++) { + if (data.supported_formats[i] == format) { + ret = true; + break; + } + } + + adf_free_overlay_engine_data(&data); + return ret; +} + +int adf_set_event(int fd, enum adf_event_type type, bool enabled) +{ + struct adf_set_event data; + + data.type = type; + data.enabled = enabled; + + int err = ioctl(fd, ADF_SET_EVENT, &data); + if (err < 0) + return -errno; + return 0; +} + +int adf_read_event(int fd, struct adf_event **event) +{ + struct adf_event header; + struct { + struct adf_event base; + uint8_t data[0]; + } *event_ret; + size_t data_size; + int ret = 0; + + int err = read(fd, &header, sizeof(header)); + if (err < 0) + return -errno; + if ((size_t)err < sizeof(header)) + return -EIO; + if (header.length < sizeof(header)) + return -EIO; + + event_ret = malloc(header.length); + if (!event_ret) + return -ENOMEM; + data_size = header.length - sizeof(header); + + memcpy(event_ret, &header, sizeof(header)); + ssize_t read_size = read(fd, &event_ret->data, data_size); + if (read_size < 0) { + ret = -errno; + goto done; + } + if ((size_t)read_size < data_size) { + ret = -EIO; + goto done; + } + + *event = &event_ret->base; + +done: + if (ret < 0) + free(event_ret); + return ret; +} + +void adf_format_str(__u32 format, char buf[ADF_FORMAT_STR_SIZE]) +{ + buf[0] = format & 0xFF; + buf[1] = (format >> 8) & 0xFF; + buf[2] = (format >> 16) & 0xFF; + buf[3] = (format >> 24) & 0xFF; + buf[4] = '\0'; +} + +static bool adf_find_simple_post_overlay_engine(struct adf_device *dev, + const __u32 *formats, size_t n_formats, + adf_id_t interface, adf_id_t *overlay_engine) +{ + adf_id_t *engs; + ssize_t n_engs = adf_overlay_engines_for_interface(dev, interface, &engs); + + if (n_engs <= 0) + return false; + + adf_id_t *filtered_engs; + ssize_t n_filtered_engs = adf_overlay_engines_filter_by_format(dev, + formats, n_formats, engs, n_engs, &filtered_engs); + free(engs); + + if (n_filtered_engs <= 0) + return false; + + *overlay_engine = filtered_engs[0]; + free(filtered_engs); + return true; +} + +static const __u32 any_rgb_format[] = { + DRM_FORMAT_C8, + DRM_FORMAT_RGB332, + DRM_FORMAT_BGR233, + DRM_FORMAT_XRGB1555, + DRM_FORMAT_XBGR1555, + DRM_FORMAT_RGBX5551, + DRM_FORMAT_BGRX5551, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_ABGR1555, + DRM_FORMAT_RGBA5551, + DRM_FORMAT_BGRA5551, + DRM_FORMAT_RGB565, + DRM_FORMAT_BGR565, + DRM_FORMAT_RGB888, + DRM_FORMAT_BGR888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_RGBX8888, + DRM_FORMAT_BGRX8888, + DRM_FORMAT_XRGB2101010, + DRM_FORMAT_XBGR2101010, + DRM_FORMAT_RGBX1010102, + DRM_FORMAT_BGRX1010102, + DRM_FORMAT_ARGB2101010, + DRM_FORMAT_ABGR2101010, + DRM_FORMAT_RGBA1010102, + DRM_FORMAT_BGRA1010102, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_RGBA8888, + DRM_FORMAT_BGRA8888, +}; + +int adf_find_simple_post_configuration(struct adf_device *dev, + const __u32 *formats, size_t n_formats, + adf_id_t *interface, adf_id_t *overlay_engine) +{ + adf_id_t *intfs; + ssize_t n_intfs = adf_interfaces(dev, &intfs); + + if (n_intfs < 0) + return n_intfs; + else if (!n_intfs) + return -ENODEV; + + adf_id_t *primary_intfs; + ssize_t n_primary_intfs = adf_interfaces_filter_by_flag(dev, + ADF_INTF_FLAG_PRIMARY, intfs, n_intfs, &primary_intfs); + free(intfs); + + if (n_primary_intfs < 0) + return n_primary_intfs; + else if (!n_primary_intfs) + return -ENODEV; + + if (!formats) { + formats = any_rgb_format; + n_formats = sizeof(any_rgb_format) / sizeof(any_rgb_format[0]); + } + + bool found = false; + ssize_t i = 0; + for (i = 0; i < n_primary_intfs; i++) { + found = adf_find_simple_post_overlay_engine(dev, formats, n_formats, + primary_intfs[i], overlay_engine); + if (found) { + *interface = primary_intfs[i]; + break; + } + } + free(primary_intfs); + + if (!found) + return -ENODEV; + + return 0; +} diff --git a/adf/libadf/include/adf/adf.h b/adf/libadf/include/adf/adf.h new file mode 100644 index 0000000..b6bda34 --- /dev/null +++ b/adf/libadf/include/adf/adf.h @@ -0,0 +1,250 @@ +/* + * 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. + */ + +#ifndef _LIBADF_ADF_H_ +#define _LIBADF_ADF_H_ + +#include <stdint.h> +#include <stdbool.h> +#include <sys/cdefs.h> +#include <sys/types.h> +#include <video/adf.h> + +typedef __u32 adf_id_t; + +struct adf_device { + adf_id_t id; + int fd; +}; + +__BEGIN_DECLS + +/** + * Enumerates all ADF devices. + * + * Returns the number of ADF devices, and sets ids to a list of device IDs. + * The caller must free() the returned list of device IDs. + * + * On error, returns -errno. + */ +ssize_t adf_devices(adf_id_t **ids); + +/** + * Opens an ADF device. + * + * On error, returns -errno. + */ +int adf_device_open(adf_id_t id, int flags, struct adf_device *dev); +/** + * Closes an ADF device. + */ +void adf_device_close(struct adf_device *dev); +/** + * Reads the ADF device data. + * + * adf_get_device_data() allocates buffers inside data, which the caller + * must free by calling adf_free_device_data(). On error, returns -errno. + */ +int adf_get_device_data(struct adf_device *dev, struct adf_device_data *data); +/** + * Frees the device data returned by adf_get_device_data(). + */ +void adf_free_device_data(struct adf_device_data *data); + +/** + * Atomically posts a new display configuration to the specified interfaces. + * + * Returns a sync fence fd that will fire when the configuration is removed + * from the screen. On error, returns -errno. + */ +int adf_device_post(struct adf_device *dev, + adf_id_t *interfaces, size_t n_interfaces, + struct adf_buffer_config *bufs, size_t n_bufs, + void *custom_data, size_t custom_data_size); +/** + * Attaches the specified interface and overlay engine. + */ +int adf_device_attach(struct adf_device *dev, adf_id_t overlay_engine, + adf_id_t interface); +/** + * Detaches the specified interface and overlay engine. + */ +int adf_device_detach(struct adf_device *dev, adf_id_t overlay_engine, + adf_id_t interface); + +/** + * Enumerates all interfaces belonging to an ADF device. + * + * The caller must free() the returned list of interface IDs. + */ +ssize_t adf_interfaces(struct adf_device *dev, adf_id_t **interfaces); + +/** + * Enumerates all interfaces which can be attached to the specified overlay + * engine. + * + * The caller must free() the returned list of interface IDs. + */ +ssize_t adf_interfaces_for_overlay_engine(struct adf_device *dev, + adf_id_t overlay_engine, adf_id_t **interfaces); +/** + * Filters a list of interfaces by type. + * + * Returns the number of matching interfaces, and sets out to a list of matching + * interface IDs. The caller must free() the returned list of interface IDs. + * + * On error, returns -errno. + */ +ssize_t adf_interfaces_filter_by_type(struct adf_device *dev, + enum adf_interface_type type, + adf_id_t *in, size_t n_in, adf_id_t **out); +/** + * Filters a list of interfaces by flag. + * + * The caller must free() the returned list of interface IDs. + */ +ssize_t adf_interfaces_filter_by_flag(struct adf_device *dev, __u32 flag, + adf_id_t *in, size_t n_in, adf_id_t **out); + +/** + * Opens an ADF interface. + * + * Returns a file descriptor. The caller must close() the fd when done. + * On error, returns -errno. + */ +int adf_interface_open(struct adf_device *dev, adf_id_t id, int flags); +/** + * Reads the interface data. + * + * adf_get_interface_data() allocates buffers inside data, which the caller + * must free by calling adf_free_interface_data(). On error, returns -errno. + */ +int adf_get_interface_data(int fd, struct adf_interface_data *data); +/** + * Frees the interface data returned by adf_get_interface_data(). + */ +void adf_free_interface_data(struct adf_interface_data *data); + +/** + * Sets the interface's DPMS mode. + */ +int adf_interface_blank(int fd, __u8 mode); +/** + * Sets the interface's display mode. + */ +int adf_interface_set_mode(int fd, struct drm_mode_modeinfo *mode); +/** + * Allocates a single-plane RGB buffer of the specified size and format. + * + * Returns a dma-buf fd. On error, returns -errno. + */ +int adf_interface_simple_buffer_alloc(int fd, __u32 w, __u32 h, + __u32 format, __u32 *offset, __u32 *pitch); +/** + * Posts a single-plane RGB buffer to the display using the specified + * overlay engine. + * + * Returns a sync fence fd that will fire when the buffer is removed + * from the screen. On error, returns -errno. + */ +int adf_interface_simple_post(int fd, adf_id_t overlay_engine, + __u32 w, __u32 h, __u32 format, int buf_fd, __u32 offset, + __u32 pitch, int acquire_fence); + +/** + * Enumerates all overlay engines belonging to an ADF device. + * + * The caller must free() the returned list of overlay engine IDs. + */ +ssize_t adf_overlay_engines(struct adf_device *dev, adf_id_t **overlay_engines); + +/** + * Enumerates all overlay engines which can be attached to the specified + * interface. + * + * The caller must free() the returned list of overlay engine IDs. + */ +ssize_t adf_overlay_engines_for_interface(struct adf_device *dev, + adf_id_t interface, adf_id_t **overlay_engines); +/** + * Filters a list of overlay engines by supported buffer format. + * + * Returns the overlay engines which support at least one of the specified + * formats. The caller must free() the returned list of overlay engine IDs. + */ +ssize_t adf_overlay_engines_filter_by_format(struct adf_device *dev, + const __u32 *formats, size_t n_formats, adf_id_t *in, size_t n_in, + adf_id_t **out); + +/** + * Opens an ADF overlay engine. + * + * Returns a file descriptor. The caller must close() the fd when done. + * On error, returns -errno. + */ +int adf_overlay_engine_open(struct adf_device *dev, adf_id_t id, int flags); +/** + * Reads the overlay engine data. + * + * adf_get_overlay_engine_data() allocates buffers inside data, which the caller + * must free by calling adf_free_overlay_engine_data(). On error, returns + * -errno. + */ +int adf_get_overlay_engine_data(int fd, struct adf_overlay_engine_data *data); +/** + * Frees the overlay engine data returned by adf_get_overlay_engine_data(). + */ +void adf_free_overlay_engine_data(struct adf_overlay_engine_data *data); + +/** + * Returns whether the overlay engine supports the specified format. + */ +bool adf_overlay_engine_supports_format(int fd, __u32 format); + +/** + * Subscribes or unsubscribes from the specified hardware event. + */ +int adf_set_event(int fd, enum adf_event_type type, bool enabled); +/** + * Reads one event from the fd, blocking if needed. + * + * The caller must free() the returned buffer. On error, returns -errno. + */ +int adf_read_event(int fd, struct adf_event **event); + +#define ADF_FORMAT_STR_SIZE 5 +/** + * Converts an ADF/DRM fourcc format to its string representation. + */ +void adf_format_str(__u32 format, char buf[ADF_FORMAT_STR_SIZE]); + +/** + * Finds an appropriate interface and overlay engine for a simple post. + * + * Specifically, finds the primary interface, and an overlay engine + * that can be attached to the primary interface and supports one of the + * specified formats. The caller may pass a NULL formats list, to indicate that + * any RGB format is acceptable. + * + * On error, returns -errno. + */ +int adf_find_simple_post_configuration(struct adf_device *dev, + const __u32 *formats, size_t n_formats, + adf_id_t *interface, adf_id_t *overlay_engine); + +__END_DECLS + +#endif /* _LIBADF_ADF_H_ */ diff --git a/adf/libadfhwc/Android.mk b/adf/libadfhwc/Android.mk new file mode 100644 index 0000000..acea322 --- /dev/null +++ b/adf/libadfhwc/Android.mk @@ -0,0 +1,25 @@ +# 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. + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := adfhwc.cpp +LOCAL_MODULE := libadfhwc +LOCAL_MODULE_TAGS := optional +LOCAL_STATIC_LIBRARIES := libadf liblog libutils +LOCAL_CFLAGS += -DLOG_TAG=\"adfhwc\" +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS) +include $(BUILD_STATIC_LIBRARY) diff --git a/adf/libadfhwc/adfhwc.cpp b/adf/libadfhwc/adfhwc.cpp new file mode 100644 index 0000000..dee3cae --- /dev/null +++ b/adf/libadfhwc/adfhwc.cpp @@ -0,0 +1,293 @@ +/* + * 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. + */ + +#include <fcntl.h> +#include <poll.h> +#include <pthread.h> +#include <sys/resource.h> + +#include <adf/adf.h> +#include <adfhwc/adfhwc.h> + +#include <cutils/log.h> +#include <utils/Vector.h> + +struct adf_hwc_helper { + adf_hwc_event_callbacks const *event_cb; + void *event_cb_data; + + pthread_t event_thread; + + android::Vector<int> intf_fds; + android::Vector<drm_mode_modeinfo> display_configs; +}; + +template<typename T> inline T min(T a, T b) { return (a < b) ? a : b; } + +int adf_eventControl(struct adf_hwc_helper *dev, int disp, int event, + int enabled) +{ + if (enabled != !!enabled) + return -EINVAL; + + if ((size_t)disp >= dev->intf_fds.size()) + return -EINVAL; + + switch (event) { + case HWC_EVENT_VSYNC: + return adf_set_event(dev->intf_fds[disp], ADF_EVENT_VSYNC, enabled); + } + + return -EINVAL; +} + +static inline int32_t dpi(uint16_t res, uint16_t size_mm) +{ + if (size_mm) + return 1000 * (res * 25.4f) / size_mm; + return 0; +} + +int adf_blank(struct adf_hwc_helper *dev, int disp, int blank) +{ + if ((size_t)disp >= dev->intf_fds.size()) + return -EINVAL; + + uint8_t dpms_mode = blank ? DRM_MODE_DPMS_OFF : DRM_MODE_DPMS_ON; + return adf_interface_blank(dev->intf_fds[disp], dpms_mode); +} + +int adf_query_display_types_supported(struct adf_hwc_helper *dev, int *value) +{ + *value = 0; + if (dev->intf_fds.size() > 0) + *value |= HWC_DISPLAY_PRIMARY_BIT; + if (dev->intf_fds.size() > 1) + *value |= HWC_DISPLAY_EXTERNAL_BIT; + + return 0; +} + +int adf_getDisplayConfigs(struct adf_hwc_helper *dev, int disp, + uint32_t *configs, size_t *numConfigs) +{ + if ((size_t)disp >= dev->intf_fds.size()) + return -EINVAL; + + adf_interface_data data; + int err = adf_get_interface_data(dev->intf_fds[disp], &data); + if (err < 0) { + ALOGE("failed to get ADF interface data: %s", strerror(err)); + return err; + } + + if (!data.hotplug_detect) + return -ENODEV; + + android::Vector<drm_mode_modeinfo *> unique_configs; + unique_configs.push_back(&data.current_mode); + for (size_t i = 0; i < data.n_available_modes; i++) + if (memcmp(&data.available_modes[i], &data.current_mode, + sizeof(data.current_mode))) + unique_configs.push_back(&data.available_modes[i]); + + for (size_t i = 0; i < min(*numConfigs, unique_configs.size()); i++) { + configs[i] = dev->display_configs.size(); + dev->display_configs.push_back(*unique_configs[i]); + } + *numConfigs = unique_configs.size(); + + adf_free_interface_data(&data); + return 0; +} + +static int32_t adf_display_attribute(const adf_interface_data &data, + const drm_mode_modeinfo &mode, const uint32_t attribute) +{ + switch (attribute) { + case HWC_DISPLAY_VSYNC_PERIOD: + if (mode.vrefresh) + return 1000000000 / mode.vrefresh; + return 0; + + case HWC_DISPLAY_WIDTH: + return mode.hdisplay; + + case HWC_DISPLAY_HEIGHT: + return mode.vdisplay; + + case HWC_DISPLAY_DPI_X: + return dpi(mode.hdisplay, data.width_mm); + + case HWC_DISPLAY_DPI_Y: + return dpi(mode.vdisplay, data.height_mm); + + default: + ALOGE("unknown display attribute %u", attribute); + return -EINVAL; + } +} + +int adf_getDisplayAttributes(struct adf_hwc_helper *dev, int disp, + uint32_t config, const uint32_t *attributes, int32_t *values) +{ + if ((size_t)disp >= dev->intf_fds.size()) + return -EINVAL; + + if (config >= dev->display_configs.size()) + return -EINVAL; + + adf_interface_data data; + int err = adf_get_interface_data(dev->intf_fds[disp], &data); + if (err < 0) { + ALOGE("failed to get ADF interface data: %s", strerror(err)); + return err; + } + + for (int i = 0; attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE; i++) + values[i] = adf_display_attribute(data, dev->display_configs[config], + attributes[i]); + + adf_free_interface_data(&data); + return 0; +} + +static void handle_adf_event(struct adf_hwc_helper *dev, int disp) +{ + adf_event *event; + int err = adf_read_event(dev->intf_fds[disp], &event); + if (err < 0) { + ALOGE("error reading event from display %d: %s", disp, strerror(err)); + return; + } + + void *vsync_temp; + adf_vsync_event *vsync; + adf_hotplug_event *hotplug; + + switch (event->type) { + case ADF_EVENT_VSYNC: + vsync_temp = event; + vsync = static_cast<adf_vsync_event *>(vsync_temp); + // casting directly to adf_vsync_event * makes g++ warn about + // potential alignment issues that don't apply here + dev->event_cb->vsync(dev->event_cb_data, disp, vsync->timestamp); + break; + case ADF_EVENT_HOTPLUG: + hotplug = reinterpret_cast<adf_hotplug_event *>(event); + dev->event_cb->hotplug(dev->event_cb_data, disp, hotplug->connected); + break; + default: + if (event->type < ADF_EVENT_DEVICE_CUSTOM) + ALOGW("unrecognized event type %u", event->type); + else if (!dev->event_cb || !dev->event_cb->custom_event) + ALOGW("unhandled event type %u", event->type); + else + dev->event_cb->custom_event(dev->event_cb_data, disp, event); + } + free(event); +} + +static void *adf_event_thread(void *data) +{ + adf_hwc_helper *dev = static_cast<adf_hwc_helper *>(data); + + setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY); + + pollfd *fds = new pollfd[dev->intf_fds.size()]; + for (size_t i = 0; i < dev->intf_fds.size(); i++) { + fds[i].fd = dev->intf_fds[i]; + fds[i].events = POLLIN | POLLPRI; + } + + while (true) { + int err = poll(fds, dev->intf_fds.size(), -1); + + if (err > 0) { + for (size_t i = 0; i < dev->intf_fds.size(); i++) + if (fds[i].revents & (POLLIN | POLLPRI)) + handle_adf_event(dev, i); + } + else if (err == -1) { + if (errno == EINTR) + break; + ALOGE("error in event thread: %s", strerror(errno)); + } + } + + delete [] fds; + return NULL; +} + +int adf_hwc_open(int *intf_fds, size_t n_intfs, + const struct adf_hwc_event_callbacks *event_cb, void *event_cb_data, + struct adf_hwc_helper **dev) +{ + if (!n_intfs) + return -EINVAL; + + adf_hwc_helper *dev_ret = new adf_hwc_helper; + dev_ret->event_cb = event_cb; + dev_ret->event_cb_data = event_cb_data; + + int ret; + + for (size_t i = 0; i < n_intfs; i++) { + int dup_intf_fd = dup(intf_fds[i]); + if (dup_intf_fd < 0) { + ALOGE("failed to dup interface fd: %s", strerror(errno)); + ret = -errno; + goto err; + } + + dev_ret->intf_fds.push_back(dup_intf_fd); + + ret = adf_set_event(dup_intf_fd, ADF_EVENT_HOTPLUG, 1); + if (ret < 0 && ret != -EINVAL) { + ALOGE("failed to enable hotplug event on display %u: %s", + i, strerror(errno)); + goto err; + } + } + + ret = pthread_create(&dev_ret->event_thread, NULL, adf_event_thread, + dev_ret); + if (ret) { + ALOGE("failed to create event thread: %s", strerror(ret)); + goto err; + } + + *dev = dev_ret; + return 0; + +err: + for (size_t i = 0; i < dev_ret->intf_fds.size(); i++) + close(dev_ret->intf_fds[i]); + + delete dev_ret; + return ret; +} + +void adf_hwc_close(struct adf_hwc_helper *dev) +{ + pthread_kill(dev->event_thread, SIGTERM); + pthread_join(dev->event_thread, NULL); + + for (size_t i = 0; i < dev->intf_fds.size(); i++) + close(dev->intf_fds[i]); + + delete dev; +} diff --git a/adf/libadfhwc/include/adfhwc/adfhwc.h b/adf/libadfhwc/include/adfhwc/adfhwc.h new file mode 100644 index 0000000..71e7624 --- /dev/null +++ b/adf/libadfhwc/include/adfhwc/adfhwc.h @@ -0,0 +1,129 @@ +/* + * 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. + */ + +#ifndef _LIBADFHWC_ADFHWC_H_ +#define _LIBADFHWC_ADFHWC_H_ + +#include <stdbool.h> +#include <stdint.h> +#include <sys/cdefs.h> +#include <video/adf.h> + +#include <hardware/hwcomposer.h> + +struct adf_hwc_helper; + +struct adf_hwc_event_callbacks { + /** + * Called on vsync (required) + */ + void (*vsync)(void *data, int disp, uint64_t timestamp); + /** + * Called on hotplug (required) + */ + void (*hotplug)(void *data, int disp, bool connected); + /** + * Called on hardware-custom ADF events (optional) + */ + void (*custom_event)(void *data, int disp, struct adf_event *event); +}; + +/** + * Converts HAL pixel formats to equivalent ADF/DRM format FourCCs. + */ +static inline uint32_t adf_fourcc_for_hal_pixel_format(int format) +{ + switch (format) { + case HAL_PIXEL_FORMAT_RGBA_8888: + return DRM_FORMAT_RGBA8888; + case HAL_PIXEL_FORMAT_RGBX_8888: + return DRM_FORMAT_RGBX8888; + case HAL_PIXEL_FORMAT_RGB_888: + return DRM_FORMAT_RGB888; + case HAL_PIXEL_FORMAT_RGB_565: + return DRM_FORMAT_RGB565; + case HAL_PIXEL_FORMAT_BGRA_8888: + return DRM_FORMAT_BGRA8888; + case HAL_PIXEL_FORMAT_YV12: + return DRM_FORMAT_YVU420; + case HAL_PIXEL_FORMAT_YCbCr_422_SP: + return DRM_FORMAT_NV16; + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + return DRM_FORMAT_NV21; + case HAL_PIXEL_FORMAT_YCbCr_422_I: + return DRM_FORMAT_YUYV; + default: + return 0; + } +} + +/** + * Converts HAL display types to equivalent ADF interface flags. + */ +static inline uint32_t adf_hwc_interface_flag_for_disp(int disp) +{ + switch (disp) { + case HWC_DISPLAY_PRIMARY: + return ADF_INTF_FLAG_PRIMARY; + case HWC_DISPLAY_EXTERNAL: + return ADF_INTF_FLAG_EXTERNAL; + default: + return 0; + } +} + +__BEGIN_DECLS + +/** + * Create a HWC helper for the specified ADF interfaces. + * + * intf_fds must be indexed by HWC display type: e.g., + * intf_fds[HWC_DISPLAY_PRIMARY] is the fd for the primary display + * interface. n_intfs must be >= 1. + * + * The caller retains ownership of the fds in intf_fds and must close() + * them when they are no longer needed. + * + * On error, returns -errno. + */ +int adf_hwc_open(int *intf_fds, size_t n_intfs, + const struct adf_hwc_event_callbacks *event_cb, void *event_cb_data, + struct adf_hwc_helper **dev); + +/** + * Destroys a HWC helper. + */ +void adf_hwc_close(struct adf_hwc_helper *dev); + +/** + * Generic implementations of common HWC ops. + * + * The HWC should not point its ops directly at these helpers. Instead, the HWC + * should provide stub ops which call these helpers after converting the + * hwc_composer_device_1* to a struct adf_hwc_helper*. + */ +int adf_eventControl(struct adf_hwc_helper *dev, int disp, int event, + int enabled); +int adf_blank(struct adf_hwc_helper *dev, int disp, int blank); +int adf_query_display_types_supported(struct adf_hwc_helper *dev, int *value); +int adf_getDisplayConfigs(struct adf_hwc_helper *dev, int disp, + uint32_t *configs, size_t *numConfigs); +int adf_getDisplayAttributes(struct adf_hwc_helper *dev, int disp, + uint32_t config, const uint32_t *attributes, int32_t *values); + +__END_DECLS + +#endif /* _LIBADFHWC_ADFHWC_H_ */ diff --git a/cpio/mkbootfs.c b/cpio/mkbootfs.c index 3569e27..7d3740c 100644 --- a/cpio/mkbootfs.c +++ b/cpio/mkbootfs.c @@ -220,6 +220,8 @@ static void _archive_dir(char *in, char *out, int ilen, int olen) free(names[i]); } free(names); + + closedir(d); } static void _archive(char *in, char *out, int ilen, int olen) diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk index 8621e9c..422a86a 100644 --- a/debuggerd/Android.mk +++ b/debuggerd/Android.mk @@ -6,14 +6,20 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - backtrace.c \ - debuggerd.c \ - getevent.c \ - tombstone.c \ - utility.c \ - $(TARGET_ARCH)/machine.c - -LOCAL_CFLAGS := -Wall -Wno-unused-parameter -std=gnu99 + backtrace.cpp \ + debuggerd.cpp \ + getevent.cpp \ + tombstone.cpp \ + utility.cpp \ + $(TARGET_ARCH)/machine.cpp \ + +LOCAL_CONLYFLAGS := -std=gnu99 +LOCAL_CPPFLAGS := -std=gnu++11 +LOCAL_CFLAGS := \ + -Wall \ + -Wno-array-bounds \ + -Werror \ + LOCAL_MODULE := debuggerd ifeq ($(ARCH_ARM_HAVE_VFP),true) @@ -24,11 +30,13 @@ LOCAL_CFLAGS += -DWITH_VFP_D32 endif # ARCH_ARM_HAVE_VFP_D32 LOCAL_SHARED_LIBRARIES := \ + libbacktrace \ + libc \ libcutils \ liblog \ - libc \ - libcorkscrew \ - libselinux + libselinux \ + +include external/stlport/libstlport.mk include $(BUILD_EXECUTABLE) @@ -38,7 +46,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_CFLAGS += -fstack-protector-all -Wno-unused-parameter -Wno-free-nonheap-object #LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_SHARED_LIBRARIES := libcutils liblog libc include $(BUILD_EXECUTABLE) diff --git a/debuggerd/arm/machine.c b/debuggerd/arm/machine.c deleted file mode 100644 index 67e3028..0000000 --- a/debuggerd/arm/machine.c +++ /dev/null @@ -1,188 +0,0 @@ -/* system/debuggerd/debuggerd.c -** -** Copyright 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. -*/ - -#include <stddef.h> -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> -#include <stdio.h> -#include <errno.h> -#include <sys/types.h> -#include <sys/ptrace.h> - -#include <corkscrew/ptrace.h> - -#include <linux/user.h> - -#include "../utility.h" -#include "../machine.h" - -/* enable to dump memory pointed to by every register */ -#define DUMP_MEMORY_FOR_ALL_REGISTERS 1 - -#ifdef WITH_VFP -#ifdef WITH_VFP_D32 -#define NUM_VFP_REGS 32 -#else -#define NUM_VFP_REGS 16 -#endif -#endif - -static void dump_memory(log_t* log, pid_t tid, uintptr_t addr, int scopeFlags) { - char code_buffer[64]; /* actual 8+1+((8+1)*4) + 1 == 45 */ - char ascii_buffer[32]; /* actual 16 + 1 == 17 */ - uintptr_t p, end; - - p = addr & ~3; - p -= 32; - if (p > addr) { - /* catch underflow */ - p = 0; - } - /* 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; - - /* Dump the code around PC as: - * addr contents ascii - * 00008d34 ef000000 e8bd0090 e1b00000 512fff1e ............../Q - * 00008d44 ea00b1f9 e92d0090 e3a070fc ef000000 ......-..p...... - */ - while (p < end) { - char* asc_out = ascii_buffer; - - sprintf(code_buffer, "%08x ", p); - - int i; - for (i = 0; i < 4; i++) { - /* - * If we see (data == -1 && errno != 0), we know that the ptrace - * call failed, probably because we're dumping memory in an - * unmapped or inaccessible page. I don't know if there's - * value in making that explicit in the output -- it likely - * just complicates parsing and clarifies nothing for the - * enlightened reader. - */ - 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++) { - /* - * Our isprint() allows high-ASCII characters that display - * differently (often badly) in different viewers, so we - * just use a simpler test. - */ - char val = (data >> (j*8)) & 0xff; - if (val >= 0x20 && val < 0x7f) { - *asc_out++ = val; - } else { - *asc_out++ = '.'; - } - } -#endif - p += 4; - } - *asc_out = '\0'; - _LOG(log, scopeFlags, " %s %s\n", code_buffer, ascii_buffer); - } -} - -/* - * If configured to do so, dump memory around *all* registers - * for the crashing thread. - */ -void dump_memory_and_code(const ptrace_context_t* context __attribute((unused)), - log_t* log, pid_t tid, bool at_fault) { - struct pt_regs regs; - if(ptrace(PTRACE_GETREGS, tid, 0, ®s)) { - return; - } - - int scopeFlags = at_fault ? SCOPE_AT_FAULT : 0; - - if (at_fault && DUMP_MEMORY_FOR_ALL_REGISTERS) { - static const char REG_NAMES[] = "r0r1r2r3r4r5r6r7r8r9slfpipsp"; - - for (int reg = 0; reg < 14; reg++) { - /* this may not be a valid way to access, but it'll do for now */ - uintptr_t addr = regs.uregs[reg]; - - /* - * Don't bother if it looks like a small int or ~= null, or if - * it's in the kernel area. - */ - if (addr < 4096 || addr >= 0xc0000000) { - continue; - } - - _LOG(log, scopeFlags | SCOPE_SENSITIVE, "\nmemory near %.2s:\n", ®_NAMES[reg * 2]); - dump_memory(log, tid, addr, scopeFlags | SCOPE_SENSITIVE); - } - } - - /* explicitly allow upload of code dump logging */ - _LOG(log, scopeFlags, "\ncode around pc:\n"); - dump_memory(log, tid, (uintptr_t)regs.ARM_pc, scopeFlags); - - if (regs.ARM_pc != regs.ARM_lr) { - _LOG(log, scopeFlags, "\ncode around lr:\n"); - dump_memory(log, tid, (uintptr_t)regs.ARM_lr, scopeFlags); - } -} - -void dump_registers(const ptrace_context_t* context __attribute((unused)), - log_t* log, pid_t tid, bool at_fault) -{ - struct pt_regs r; - int scopeFlags = at_fault ? SCOPE_AT_FAULT : 0; - - if(ptrace(PTRACE_GETREGS, tid, 0, &r)) { - _LOG(log, scopeFlags, "cannot get registers: %s\n", strerror(errno)); - return; - } - - _LOG(log, scopeFlags, " r0 %08x r1 %08x r2 %08x r3 %08x\n", - (uint32_t)r.ARM_r0, (uint32_t)r.ARM_r1, (uint32_t)r.ARM_r2, (uint32_t)r.ARM_r3); - _LOG(log, scopeFlags, " r4 %08x r5 %08x r6 %08x r7 %08x\n", - (uint32_t)r.ARM_r4, (uint32_t)r.ARM_r5, (uint32_t)r.ARM_r6, (uint32_t)r.ARM_r7); - _LOG(log, scopeFlags, " r8 %08x r9 %08x sl %08x fp %08x\n", - (uint32_t)r.ARM_r8, (uint32_t)r.ARM_r9, (uint32_t)r.ARM_r10, (uint32_t)r.ARM_fp); - _LOG(log, scopeFlags, " ip %08x sp %08x lr %08x pc %08x cpsr %08x\n", - (uint32_t)r.ARM_ip, (uint32_t)r.ARM_sp, (uint32_t)r.ARM_lr, - (uint32_t)r.ARM_pc, (uint32_t)r.ARM_cpsr); - -#ifdef WITH_VFP - struct user_vfp vfp_regs; - int i; - - if(ptrace(PTRACE_GETVFPREGS, tid, 0, &vfp_regs)) { - _LOG(log, scopeFlags, "cannot get registers: %s\n", strerror(errno)); - return; - } - - for (i = 0; i < NUM_VFP_REGS; i += 2) { - _LOG(log, scopeFlags, " d%-2d %016llx d%-2d %016llx\n", - i, vfp_regs.fpregs[i], i+1, vfp_regs.fpregs[i+1]); - } - _LOG(log, scopeFlags, " scr %08lx\n", vfp_regs.fpscr); -#endif -} diff --git a/debuggerd/arm/machine.cpp b/debuggerd/arm/machine.cpp new file mode 100644 index 0000000..3fba6db --- /dev/null +++ b/debuggerd/arm/machine.cpp @@ -0,0 +1,172 @@ +/* + * + * Copyright 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. + */ + +#include <errno.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ptrace.h> +#include <sys/types.h> +#include <sys/user.h> + +#include "../utility.h" +#include "../machine.h" + +// enable to dump memory pointed to by every register +#define DUMP_MEMORY_FOR_ALL_REGISTERS 1 + +#ifdef WITH_VFP +#ifdef WITH_VFP_D32 +#define NUM_VFP_REGS 32 +#else +#define NUM_VFP_REGS 16 +#endif +#endif + +static void dump_memory(log_t* log, pid_t tid, uintptr_t addr, int scope_flags) { + char code_buffer[64]; // actual 8+1+((8+1)*4) + 1 == 45 + char ascii_buffer[32]; // actual 16 + 1 == 17 + uintptr_t p, end; + + p = addr & ~3; + p -= 32; + if (p > addr) { + // catch underflow + p = 0; + } + // 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; + + // Dump the code around PC as: + // addr contents ascii + // 00008d34 ef000000 e8bd0090 e1b00000 512fff1e ............../Q + // 00008d44 ea00b1f9 e92d0090 e3a070fc ef000000 ......-..p...... + while (p < end) { + char* asc_out = ascii_buffer; + + sprintf(code_buffer, "%08x ", p); + + int i; + for (i = 0; i < 4; i++) { + // If we see (data == -1 && errno != 0), we know that the ptrace + // call failed, probably because we're dumping memory in an + // unmapped or inaccessible page. I don't know if there's + // value in making that explicit in the output -- it likely + // just complicates parsing and clarifies nothing for the + // enlightened reader. + long data = ptrace(PTRACE_PEEKTEXT, tid, reinterpret_cast<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++) { + // Our isprint() allows high-ASCII characters that display + // differently (often badly) in different viewers, so we + // just use a simpler test. + char val = (data >> (j*8)) & 0xff; + if (val >= 0x20 && val < 0x7f) { + *asc_out++ = val; + } else { + *asc_out++ = '.'; + } + } +#endif + p += 4; + } + *asc_out = '\0'; + _LOG(log, scope_flags, " %s %s\n", code_buffer, ascii_buffer); + } +} + +// If configured to do so, dump memory around *all* registers +// for the crashing thread. +void dump_memory_and_code(log_t* log, pid_t tid, int scope_flags) { + struct pt_regs regs; + if (ptrace(PTRACE_GETREGS, tid, 0, ®s)) { + return; + } + + if (IS_AT_FAULT(scope_flags) && DUMP_MEMORY_FOR_ALL_REGISTERS) { + static const char REG_NAMES[] = "r0r1r2r3r4r5r6r7r8r9slfpipsp"; + + for (int reg = 0; reg < 14; reg++) { + // this may not be a valid way to access, but it'll do for now + uintptr_t addr = regs.uregs[reg]; + + // Don't bother if it looks like a small int or ~= null, or if + // it's in the kernel area. + if (addr < 4096 || addr >= 0xc0000000) { + continue; + } + + _LOG(log, scope_flags | SCOPE_SENSITIVE, "\nmemory near %.2s:\n", ®_NAMES[reg * 2]); + dump_memory(log, tid, addr, scope_flags | SCOPE_SENSITIVE); + } + } + + // explicitly allow upload of code dump logging + _LOG(log, scope_flags, "\ncode around pc:\n"); + dump_memory(log, tid, static_cast<uintptr_t>(regs.ARM_pc), scope_flags); + + if (regs.ARM_pc != regs.ARM_lr) { + _LOG(log, scope_flags, "\ncode around lr:\n"); + dump_memory(log, tid, static_cast<uintptr_t>(regs.ARM_lr), scope_flags); + } +} + +void dump_registers(log_t* log, pid_t tid, int scope_flags) { + struct pt_regs r; + if (ptrace(PTRACE_GETREGS, tid, 0, &r)) { + _LOG(log, scope_flags, "cannot get registers: %s\n", strerror(errno)); + return; + } + + _LOG(log, scope_flags, " r0 %08x r1 %08x r2 %08x r3 %08x\n", + static_cast<uint32_t>(r.ARM_r0), static_cast<uint32_t>(r.ARM_r1), + static_cast<uint32_t>(r.ARM_r2), static_cast<uint32_t>(r.ARM_r3)); + _LOG(log, scope_flags, " r4 %08x r5 %08x r6 %08x r7 %08x\n", + static_cast<uint32_t>(r.ARM_r4), static_cast<uint32_t>(r.ARM_r5), + static_cast<uint32_t>(r.ARM_r6), static_cast<uint32_t>(r.ARM_r7)); + _LOG(log, scope_flags, " r8 %08x r9 %08x sl %08x fp %08x\n", + static_cast<uint32_t>(r.ARM_r8), static_cast<uint32_t>(r.ARM_r9), + static_cast<uint32_t>(r.ARM_r10), static_cast<uint32_t>(r.ARM_fp)); + _LOG(log, scope_flags, " ip %08x sp %08x lr %08x pc %08x cpsr %08x\n", + static_cast<uint32_t>(r.ARM_ip), static_cast<uint32_t>(r.ARM_sp), + static_cast<uint32_t>(r.ARM_lr), static_cast<uint32_t>(r.ARM_pc), + static_cast<uint32_t>(r.ARM_cpsr)); + +#ifdef WITH_VFP + struct user_vfp vfp_regs; + int i; + + if (ptrace(PTRACE_GETVFPREGS, tid, 0, &vfp_regs)) { + _LOG(log, scope_flags, "cannot get registers: %s\n", strerror(errno)); + return; + } + + for (i = 0; i < NUM_VFP_REGS; i += 2) { + _LOG(log, scope_flags, " d%-2d %016llx d%-2d %016llx\n", + i, vfp_regs.fpregs[i], i+1, vfp_regs.fpregs[i+1]); + } + _LOG(log, scope_flags, " scr %08lx\n", vfp_regs.fpscr); +#endif +} diff --git a/debuggerd/backtrace.c b/debuggerd/backtrace.c deleted file mode 100644 index f42f24c..0000000 --- a/debuggerd/backtrace.c +++ /dev/null @@ -1,150 +0,0 @@ -/* - * 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 <stddef.h> -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> -#include <stdio.h> -#include <time.h> -#include <errno.h> -#include <limits.h> -#include <dirent.h> -#include <unistd.h> -#include <sys/types.h> -#include <sys/ptrace.h> - -#include <corkscrew/backtrace.h> - -#include "tombstone.h" -#include "utility.h" - -#define STACK_DEPTH 32 - -static void dump_process_header(log_t* log, pid_t pid) { - char path[PATH_MAX]; - char procnamebuf[1024]; - char* procname = NULL; - FILE* fp; - - snprintf(path, sizeof(path), "/proc/%d/cmdline", pid); - if ((fp = fopen(path, "r"))) { - procname = fgets(procnamebuf, sizeof(procnamebuf), fp); - fclose(fp); - } - - time_t t = time(NULL); - struct tm tm; - localtime_r(&t, &tm); - char timestr[64]; - strftime(timestr, sizeof(timestr), "%F %T", &tm); - _LOG(log, SCOPE_AT_FAULT, "\n\n----- pid %d at %s -----\n", pid, timestr); - - if (procname) { - _LOG(log, SCOPE_AT_FAULT, "Cmd line: %s\n", procname); - } -} - -static void dump_process_footer(log_t* log, pid_t pid) { - _LOG(log, SCOPE_AT_FAULT, "\n----- end %d -----\n", pid); -} - -static void dump_thread(log_t* log, pid_t tid, ptrace_context_t* context, bool attached, - bool* detach_failed, int* total_sleep_time_usec) { - char path[PATH_MAX]; - char threadnamebuf[1024]; - char* threadname = NULL; - FILE* fp; - - snprintf(path, sizeof(path), "/proc/%d/comm", tid); - if ((fp = fopen(path, "r"))) { - threadname = fgets(threadnamebuf, sizeof(threadnamebuf), fp); - fclose(fp); - if (threadname) { - size_t len = strlen(threadname); - if (len && threadname[len - 1] == '\n') { - threadname[len - 1] = '\0'; - } - } - } - - _LOG(log, SCOPE_AT_FAULT, "\n\"%s\" sysTid=%d\n", - threadname ? threadname : "<unknown>", tid); - - if (!attached && ptrace(PTRACE_ATTACH, tid, 0, 0) < 0) { - _LOG(log, SCOPE_AT_FAULT, "Could not attach to thread: %s\n", strerror(errno)); - return; - } - - wait_for_stop(tid, total_sleep_time_usec); - - backtrace_frame_t backtrace[STACK_DEPTH]; - ssize_t frames = unwind_backtrace_ptrace(tid, context, backtrace, 0, STACK_DEPTH); - if (frames <= 0) { - _LOG(log, SCOPE_AT_FAULT, "Could not obtain stack trace for thread.\n"); - } else { - backtrace_symbol_t backtrace_symbols[STACK_DEPTH]; - get_backtrace_symbols_ptrace(context, backtrace, frames, backtrace_symbols); - for (size_t i = 0; i < (size_t)frames; i++) { - char line[MAX_BACKTRACE_LINE_LENGTH]; - format_backtrace_line(i, &backtrace[i], &backtrace_symbols[i], - line, MAX_BACKTRACE_LINE_LENGTH); - _LOG(log, SCOPE_AT_FAULT, " %s\n", line); - } - free_backtrace_symbols(backtrace_symbols, frames); - } - - if (!attached && ptrace(PTRACE_DETACH, tid, 0, 0) != 0) { - LOG("ptrace detach from %d failed: %s\n", tid, strerror(errno)); - *detach_failed = true; - } -} - -void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed, - int* total_sleep_time_usec) { - log_t log; - log.tfd = fd; - log.amfd = amfd; - log.quiet = true; - - ptrace_context_t* context = load_ptrace_context(tid); - dump_process_header(&log, pid); - dump_thread(&log, tid, context, true, detach_failed, total_sleep_time_usec); - - char task_path[64]; - snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid); - DIR* d = opendir(task_path); - if (d != NULL) { - struct dirent* de = NULL; - while ((de = readdir(d)) != NULL) { - if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) { - continue; - } - - char* end; - pid_t new_tid = strtoul(de->d_name, &end, 10); - if (*end || new_tid == tid) { - continue; - } - - dump_thread(&log, new_tid, context, false, detach_failed, total_sleep_time_usec); - } - closedir(d); - } - - dump_process_footer(&log, pid); - free_ptrace_context(context); -} diff --git a/debuggerd/backtrace.cpp b/debuggerd/backtrace.cpp new file mode 100644 index 0000000..d388348 --- /dev/null +++ b/debuggerd/backtrace.cpp @@ -0,0 +1,141 @@ +/* + * 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 <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <time.h> +#include <errno.h> +#include <limits.h> +#include <dirent.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/ptrace.h> + +#include <backtrace/Backtrace.h> +#include <UniquePtr.h> + +#include "backtrace.h" +#include "utility.h" + +static void dump_process_header(log_t* log, pid_t pid) { + char path[PATH_MAX]; + char procnamebuf[1024]; + char* procname = NULL; + FILE* fp; + + snprintf(path, sizeof(path), "/proc/%d/cmdline", pid); + if ((fp = fopen(path, "r"))) { + procname = fgets(procnamebuf, sizeof(procnamebuf), fp); + fclose(fp); + } + + time_t t = time(NULL); + struct tm tm; + localtime_r(&t, &tm); + char timestr[64]; + strftime(timestr, sizeof(timestr), "%F %T", &tm); + _LOG(log, SCOPE_AT_FAULT, "\n\n----- pid %d at %s -----\n", pid, timestr); + + if (procname) { + _LOG(log, SCOPE_AT_FAULT, "Cmd line: %s\n", procname); + } +} + +static void dump_process_footer(log_t* log, pid_t pid) { + _LOG(log, SCOPE_AT_FAULT, "\n----- end %d -----\n", pid); +} + +static void dump_thread( + log_t* log, pid_t tid, bool attached, bool* detach_failed, int* total_sleep_time_usec) { + char path[PATH_MAX]; + char threadnamebuf[1024]; + char* threadname = NULL; + FILE* fp; + + snprintf(path, sizeof(path), "/proc/%d/comm", tid); + if ((fp = fopen(path, "r"))) { + threadname = fgets(threadnamebuf, sizeof(threadnamebuf), fp); + fclose(fp); + if (threadname) { + size_t len = strlen(threadname); + if (len && threadname[len - 1] == '\n') { + threadname[len - 1] = '\0'; + } + } + } + + _LOG(log, SCOPE_AT_FAULT, "\n\"%s\" sysTid=%d\n", threadname ? threadname : "<unknown>", tid); + + if (!attached && ptrace(PTRACE_ATTACH, tid, 0, 0) < 0) { + _LOG(log, SCOPE_AT_FAULT, "Could not attach to thread: %s\n", strerror(errno)); + return; + } + + wait_for_stop(tid, total_sleep_time_usec); + + UniquePtr<Backtrace> backtrace(Backtrace::Create(tid, BACKTRACE_CURRENT_THREAD)); + if (backtrace->Unwind(0)) { + dump_backtrace_to_log(backtrace.get(), log, SCOPE_AT_FAULT, " "); + } + + if (!attached && ptrace(PTRACE_DETACH, tid, 0, 0) != 0) { + LOG("ptrace detach from %d failed: %s\n", tid, strerror(errno)); + *detach_failed = true; + } +} + +void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed, + int* total_sleep_time_usec) { + log_t log; + log.tfd = fd; + log.amfd = amfd; + log.quiet = true; + + dump_process_header(&log, pid); + dump_thread(&log, tid, true, detach_failed, total_sleep_time_usec); + + char task_path[64]; + snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid); + DIR* d = opendir(task_path); + if (d != NULL) { + struct dirent* de = NULL; + while ((de = readdir(d)) != NULL) { + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) { + continue; + } + + char* end; + pid_t new_tid = strtoul(de->d_name, &end, 10); + if (*end || new_tid == tid) { + continue; + } + + dump_thread(&log, new_tid, false, detach_failed, total_sleep_time_usec); + } + closedir(d); + } + + dump_process_footer(&log, pid); +} + +void dump_backtrace_to_log(Backtrace* backtrace, log_t* log, + int scope_flags, const char* prefix) { + for (size_t i = 0; i < backtrace->NumFrames(); i++) { + _LOG(log, scope_flags, "%s%s\n", prefix, backtrace->FormatFrameData(i).c_str()); + } +} diff --git a/debuggerd/backtrace.h b/debuggerd/backtrace.h index c5c786a..2ec8afb 100644 --- a/debuggerd/backtrace.h +++ b/debuggerd/backtrace.h @@ -17,15 +17,19 @@ #ifndef _DEBUGGERD_BACKTRACE_H #define _DEBUGGERD_BACKTRACE_H -#include <stddef.h> -#include <stdbool.h> #include <sys/types.h> -#include <corkscrew/ptrace.h> +#include "utility.h" -/* Dumps a backtrace using a format similar to what Dalvik uses so that the result - * can be intermixed in a bug report. */ +class Backtrace; + +// Dumps a backtrace using a format similar to what Dalvik uses so that the result +// can be intermixed in a bug report. void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed, - int* total_sleep_time_usec); + int* total_sleep_time_usec); + +/* Dumps the backtrace in the backtrace data structure to the log. */ +void dump_backtrace_to_log(Backtrace* backtrace, log_t* log, + int scope_flags, const char* prefix); #endif // _DEBUGGERD_BACKTRACE_H diff --git a/debuggerd/debuggerd.c b/debuggerd/debuggerd.c deleted file mode 100644 index 756f7bb..0000000 --- a/debuggerd/debuggerd.c +++ /dev/null @@ -1,539 +0,0 @@ -/* system/debuggerd/debuggerd.c -** -** Copyright 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. -*/ - -#include <stdio.h> -#include <errno.h> -#include <signal.h> -#include <pthread.h> -#include <stdarg.h> -#include <fcntl.h> -#include <sys/types.h> -#include <dirent.h> -#include <time.h> - -#include <sys/ptrace.h> -#include <sys/wait.h> -#include <sys/exec_elf.h> -#include <sys/stat.h> -#include <sys/poll.h> - -#include <log/logd.h> -#include <log/logger.h> - -#include <cutils/sockets.h> -#include <cutils/properties.h> -#include <cutils/debugger.h> - -#include <corkscrew/backtrace.h> - -#include <linux/input.h> - -#include <private/android_filesystem_config.h> - -#include "backtrace.h" -#include "getevent.h" -#include "tombstone.h" -#include "utility.h" - -typedef struct { - debugger_action_t action; - pid_t pid, tid; - uid_t uid, gid; - uintptr_t abort_msg_address; -} debugger_request_t; - -static int -write_string(const char* file, const char* string) -{ - int len; - int fd; - ssize_t amt; - fd = open(file, O_RDWR); - len = strlen(string); - if (fd < 0) - return -errno; - amt = write(fd, string, len); - close(fd); - return amt >= 0 ? 0 : -errno; -} - -static -void init_debug_led(void) -{ - // trout leds - write_string("/sys/class/leds/red/brightness", "0"); - write_string("/sys/class/leds/green/brightness", "0"); - write_string("/sys/class/leds/blue/brightness", "0"); - write_string("/sys/class/leds/red/device/blink", "0"); - // sardine leds - write_string("/sys/class/leds/left/cadence", "0,0"); -} - -static -void enable_debug_led(void) -{ - // trout leds - write_string("/sys/class/leds/red/brightness", "255"); - // sardine leds - write_string("/sys/class/leds/left/cadence", "1,0"); -} - -static -void disable_debug_led(void) -{ - // trout leds - write_string("/sys/class/leds/red/brightness", "0"); - // sardine leds - write_string("/sys/class/leds/left/cadence", "0,0"); -} - -static void wait_for_user_action(pid_t pid) { - /* First log a helpful message */ - LOG( "********************************************************\n" - "* Process %d has been suspended while crashing. To\n" - "* attach gdbserver for a gdb connection on port 5039\n" - "* and start gdbclient:\n" - "*\n" - "* gdbclient app_process :5039 %d\n" - "*\n" - "* Wait for gdb to start, then press HOME or VOLUME DOWN key\n" - "* to let the process continue crashing.\n" - "********************************************************\n", - pid, pid); - - /* wait for HOME or VOLUME DOWN key */ - if (init_getevent() == 0) { - int ms = 1200 / 10; - int dit = 1; - int dah = 3*dit; - int _ = -dit; - int ___ = 3*_; - int _______ = 7*_; - const signed char codes[] = { - dit,_,dit,_,dit,___,dah,_,dah,_,dah,___,dit,_,dit,_,dit,_______ - }; - size_t s = 0; - struct input_event e; - bool done = false; - init_debug_led(); - enable_debug_led(); - do { - int timeout = abs((int)(codes[s])) * ms; - int res = get_event(&e, timeout); - if (res == 0) { - if (e.type == EV_KEY - && (e.code == KEY_HOME || e.code == KEY_VOLUMEDOWN) - && e.value == 0) { - done = true; - } - } else if (res == 1) { - if (++s >= sizeof(codes)/sizeof(*codes)) - s = 0; - if (codes[s] > 0) { - enable_debug_led(); - } else { - disable_debug_led(); - } - } - } while (!done); - uninit_getevent(); - } - - /* don't forget to turn debug led off */ - disable_debug_led(); - LOG("debuggerd resuming process %d", pid); -} - -static int get_process_info(pid_t tid, pid_t* out_pid, uid_t* out_uid, uid_t* out_gid) { - char path[64]; - snprintf(path, sizeof(path), "/proc/%d/status", tid); - - FILE* fp = fopen(path, "r"); - if (!fp) { - return -1; - } - - int fields = 0; - char line[1024]; - while (fgets(line, sizeof(line), fp)) { - size_t len = strlen(line); - if (len > 6 && !memcmp(line, "Tgid:\t", 6)) { - *out_pid = atoi(line + 6); - fields |= 1; - } else if (len > 5 && !memcmp(line, "Uid:\t", 5)) { - *out_uid = atoi(line + 5); - fields |= 2; - } else if (len > 5 && !memcmp(line, "Gid:\t", 5)) { - *out_gid = atoi(line + 5); - fields |= 4; - } - } - fclose(fp); - return fields == 7 ? 0 : -1; -} - -static int read_request(int fd, debugger_request_t* out_request) { - struct ucred cr; - int len = sizeof(cr); - int status = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len); - if (status != 0) { - LOG("cannot get credentials\n"); - return -1; - } - - XLOG("reading tid\n"); - fcntl(fd, F_SETFL, O_NONBLOCK); - - struct pollfd pollfds[1]; - pollfds[0].fd = fd; - pollfds[0].events = POLLIN; - pollfds[0].revents = 0; - status = TEMP_FAILURE_RETRY(poll(pollfds, 1, 3000)); - if (status != 1) { - LOG("timed out reading tid (from pid=%d uid=%d)\n", cr.pid, cr.uid); - return -1; - } - - debugger_msg_t msg; - memset(&msg, 0, sizeof(msg)); - status = TEMP_FAILURE_RETRY(read(fd, &msg, sizeof(msg))); - if (status < 0) { - LOG("read failure? %s (pid=%d uid=%d)\n", - strerror(errno), cr.pid, cr.uid); - return -1; - } - if (status == sizeof(debugger_msg_t)) { - XLOG("crash request of size %d abort_msg_address=%#08x\n", status, msg.abort_msg_address); - } else { - LOG("invalid crash request of size %d (from pid=%d uid=%d)\n", - status, cr.pid, cr.uid); - return -1; - } - - out_request->action = msg.action; - out_request->tid = msg.tid; - out_request->pid = cr.pid; - out_request->uid = cr.uid; - out_request->gid = cr.gid; - out_request->abort_msg_address = msg.abort_msg_address; - - if (msg.action == DEBUGGER_ACTION_CRASH) { - /* Ensure that the tid reported by the crashing process is valid. */ - char buf[64]; - struct stat s; - snprintf(buf, sizeof buf, "/proc/%d/task/%d", out_request->pid, out_request->tid); - if(stat(buf, &s)) { - LOG("tid %d does not exist in pid %d. ignoring debug request\n", - out_request->tid, out_request->pid); - return -1; - } - } else if (cr.uid == 0 - || (cr.uid == AID_SYSTEM && msg.action == DEBUGGER_ACTION_DUMP_BACKTRACE)) { - /* Only root or system can ask us to attach to any process and dump it explicitly. - * However, system is only allowed to collect backtraces but cannot dump tombstones. */ - status = get_process_info(out_request->tid, &out_request->pid, - &out_request->uid, &out_request->gid); - if (status < 0) { - LOG("tid %d does not exist. ignoring explicit dump request\n", - out_request->tid); - return -1; - } - } else { - /* No one else is allowed to dump arbitrary processes. */ - return -1; - } - return 0; -} - -static bool should_attach_gdb(debugger_request_t* request) { - if (request->action == DEBUGGER_ACTION_CRASH) { - char value[PROPERTY_VALUE_MAX]; - property_get("debug.db.uid", value, "-1"); - int debug_uid = atoi(value); - return debug_uid >= 0 && request->uid <= (uid_t)debug_uid; - } - return false; -} - -static void handle_request(int fd) { - XLOG("handle_request(%d)\n", fd); - - debugger_request_t request; - memset(&request, 0, sizeof(request)); - int status = read_request(fd, &request); - if (!status) { - XLOG("BOOM: pid=%d uid=%d gid=%d tid=%d\n", - request.pid, request.uid, request.gid, request.tid); - - /* At this point, the thread that made the request is blocked in - * a read() call. If the thread has crashed, then this gives us - * time to PTRACE_ATTACH to it before it has a chance to really fault. - * - * The PTRACE_ATTACH sends a SIGSTOP to the target process, but it - * won't necessarily have stopped by the time ptrace() returns. (We - * currently assume it does.) We write to the file descriptor to - * ensure that it can run as soon as we call PTRACE_CONT below. - * See details in bionic/libc/linker/debugger.c, in function - * debugger_signal_handler(). - */ - if (ptrace(PTRACE_ATTACH, request.tid, 0, 0)) { - LOG("ptrace attach failed: %s\n", strerror(errno)); - } else { - bool detach_failed = false; - bool attach_gdb = should_attach_gdb(&request); - if (TEMP_FAILURE_RETRY(write(fd, "\0", 1)) != 1) { - LOG("failed responding to client: %s\n", strerror(errno)); - } else { - char* tombstone_path = NULL; - - if (request.action == DEBUGGER_ACTION_CRASH) { - close(fd); - fd = -1; - } - - int total_sleep_time_usec = 0; - for (;;) { - int signal = wait_for_signal(request.tid, &total_sleep_time_usec); - if (signal < 0) { - break; - } - - switch (signal) { - case SIGSTOP: - if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) { - XLOG("stopped -- dumping to tombstone\n"); - tombstone_path = engrave_tombstone(request.pid, request.tid, - signal, request.abort_msg_address, true, true, &detach_failed, - &total_sleep_time_usec); - } else if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE) { - XLOG("stopped -- dumping to fd\n"); - dump_backtrace(fd, -1, - request.pid, request.tid, &detach_failed, - &total_sleep_time_usec); - } else { - XLOG("stopped -- continuing\n"); - status = ptrace(PTRACE_CONT, request.tid, 0, 0); - if (status) { - LOG("ptrace continue failed: %s\n", strerror(errno)); - } - continue; /* loop again */ - } - break; - - case SIGILL: - case SIGABRT: - case SIGBUS: - case SIGFPE: - case SIGSEGV: - case SIGPIPE: -#ifdef SIGSTKFLT - case SIGSTKFLT: -#endif - { - XLOG("stopped -- fatal signal\n"); - /* - * Send a SIGSTOP to the process to make all of - * the non-signaled threads stop moving. Without - * this we get a lot of "ptrace detach failed: - * No such process". - */ - kill(request.pid, SIGSTOP); - /* don't dump sibling threads when attaching to GDB because it - * makes the process less reliable, apparently... */ - tombstone_path = engrave_tombstone(request.pid, request.tid, - signal, request.abort_msg_address, !attach_gdb, false, - &detach_failed, &total_sleep_time_usec); - break; - } - - default: - XLOG("stopped -- unexpected signal\n"); - LOG("process stopped due to unexpected signal %d\n", signal); - break; - } - break; - } - - if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) { - if (tombstone_path) { - write(fd, tombstone_path, strlen(tombstone_path)); - } - close(fd); - fd = -1; - } - free(tombstone_path); - } - - XLOG("detaching\n"); - if (attach_gdb) { - /* stop the process so we can debug */ - kill(request.pid, SIGSTOP); - - /* detach so we can attach gdbserver */ - if (ptrace(PTRACE_DETACH, request.tid, 0, 0)) { - LOG("ptrace detach from %d failed: %s\n", request.tid, strerror(errno)); - detach_failed = true; - } - - /* - * if debug.db.uid is set, its value indicates if we should wait - * for user action for the crashing process. - * in this case, we log a message and turn the debug LED on - * waiting for a gdb connection (for instance) - */ - wait_for_user_action(request.pid); - } else { - /* just detach */ - if (ptrace(PTRACE_DETACH, request.tid, 0, 0)) { - LOG("ptrace detach from %d failed: %s\n", request.tid, strerror(errno)); - detach_failed = true; - } - } - - /* resume stopped process (so it can crash in peace). */ - kill(request.pid, SIGCONT); - - /* If we didn't successfully detach, we're still the parent, and the - * actual parent won't receive a death notification via wait(2). At this point - * there's not much we can do about that. */ - if (detach_failed) { - LOG("debuggerd committing suicide to free the zombie!\n"); - kill(getpid(), SIGKILL); - } - } - - } - if (fd >= 0) { - close(fd); - } -} - -static int do_server() { - int s; - struct sigaction act; - int logsocket = -1; - - /* - * debuggerd crashes can't be reported to debuggerd. Reset all of the - * crash handlers. - */ - signal(SIGILL, SIG_DFL); - signal(SIGABRT, SIG_DFL); - signal(SIGBUS, SIG_DFL); - signal(SIGFPE, SIG_DFL); - signal(SIGSEGV, SIG_DFL); -#ifdef SIGSTKFLT - signal(SIGSTKFLT, SIG_DFL); -#endif - - // Ignore failed writes to closed sockets - signal(SIGPIPE, SIG_IGN); - - logsocket = socket_local_client("logd", - ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_DGRAM); - if(logsocket < 0) { - logsocket = -1; - } else { - fcntl(logsocket, F_SETFD, FD_CLOEXEC); - } - - act.sa_handler = SIG_DFL; - sigemptyset(&act.sa_mask); - sigaddset(&act.sa_mask,SIGCHLD); - act.sa_flags = SA_NOCLDWAIT; - sigaction(SIGCHLD, &act, 0); - - s = socket_local_server(DEBUGGER_SOCKET_NAME, - ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); - if(s < 0) return 1; - fcntl(s, F_SETFD, FD_CLOEXEC); - - LOG("debuggerd: " __DATE__ " " __TIME__ "\n"); - - for(;;) { - struct sockaddr addr; - socklen_t alen; - int fd; - - alen = sizeof(addr); - XLOG("waiting for connection\n"); - fd = accept(s, &addr, &alen); - if(fd < 0) { - XLOG("accept failed: %s\n", strerror(errno)); - continue; - } - - fcntl(fd, F_SETFD, FD_CLOEXEC); - - handle_request(fd); - } - return 0; -} - -static int do_explicit_dump(pid_t tid, bool dump_backtrace) { - fprintf(stdout, "Sending request to dump task %d.\n", tid); - - if (dump_backtrace) { - fflush(stdout); - if (dump_backtrace_to_file(tid, fileno(stdout)) < 0) { - fputs("Error dumping backtrace.\n", stderr); - return 1; - } - } else { - char tombstone_path[PATH_MAX]; - if (dump_tombstone(tid, tombstone_path, sizeof(tombstone_path)) < 0) { - fputs("Error dumping tombstone.\n", stderr); - return 1; - } - fprintf(stderr, "Tombstone written to: %s\n", tombstone_path); - } - return 0; -} - -static void usage() { - fputs("Usage: -b [<tid>]\n" - " -b dump backtrace to console, otherwise dump full tombstone file\n" - "\n" - "If tid specified, sends a request to debuggerd to dump that task.\n" - "Otherwise, starts the debuggerd server.\n", stderr); -} - -int main(int argc, char** argv) { - if (argc == 1) { - return do_server(); - } - - bool dump_backtrace = false; - bool have_tid = false; - pid_t tid = 0; - for (int i = 1; i < argc; i++) { - if (!strcmp(argv[i], "-b")) { - dump_backtrace = true; - } else if (!have_tid) { - tid = atoi(argv[i]); - have_tid = true; - } else { - usage(); - return 1; - } - } - if (!have_tid) { - usage(); - return 1; - } - return do_explicit_dump(tid, dump_backtrace); -} diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp new file mode 100644 index 0000000..de8ba9d --- /dev/null +++ b/debuggerd/debuggerd.cpp @@ -0,0 +1,514 @@ +/* + * Copyright 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. + */ + +#include <stdio.h> +#include <errno.h> +#include <signal.h> +#include <pthread.h> +#include <stdarg.h> +#include <fcntl.h> +#include <sys/types.h> +#include <dirent.h> +#include <time.h> + +#include <sys/ptrace.h> +#include <sys/wait.h> +#include <elf.h> +#include <sys/stat.h> +#include <sys/poll.h> + +#include <log/logd.h> +#include <log/logger.h> + +#include <cutils/sockets.h> +#include <cutils/properties.h> +#include <cutils/debugger.h> + +#include <linux/input.h> + +#include <private/android_filesystem_config.h> + +#include "backtrace.h" +#include "getevent.h" +#include "tombstone.h" +#include "utility.h" + +typedef struct { + debugger_action_t action; + pid_t pid, tid; + uid_t uid, gid; + uintptr_t abort_msg_address; +} debugger_request_t; + +static int write_string(const char* file, const char* string) { + int len; + int fd; + ssize_t amt; + fd = open(file, O_RDWR); + len = strlen(string); + if (fd < 0) + return -errno; + amt = write(fd, string, len); + close(fd); + return amt >= 0 ? 0 : -errno; +} + +static void init_debug_led() { + // trout leds + write_string("/sys/class/leds/red/brightness", "0"); + write_string("/sys/class/leds/green/brightness", "0"); + write_string("/sys/class/leds/blue/brightness", "0"); + write_string("/sys/class/leds/red/device/blink", "0"); + // sardine leds + write_string("/sys/class/leds/left/cadence", "0,0"); +} + +static void enable_debug_led() { + // trout leds + write_string("/sys/class/leds/red/brightness", "255"); + // sardine leds + write_string("/sys/class/leds/left/cadence", "1,0"); +} + +static void disable_debug_led() { + // trout leds + write_string("/sys/class/leds/red/brightness", "0"); + // sardine leds + write_string("/sys/class/leds/left/cadence", "0,0"); +} + +static void wait_for_user_action(pid_t pid) { + // First log a helpful message + LOG( "********************************************************\n" + "* Process %d has been suspended while crashing. To\n" + "* attach gdbserver for a gdb connection on port 5039\n" + "* and start gdbclient:\n" + "*\n" + "* gdbclient app_process :5039 %d\n" + "*\n" + "* Wait for gdb to start, then press HOME or VOLUME DOWN key\n" + "* to let the process continue crashing.\n" + "********************************************************\n", + pid, pid); + + // wait for HOME or VOLUME DOWN key + if (init_getevent() == 0) { + int ms = 1200 / 10; + int dit = 1; + int dah = 3*dit; + int _ = -dit; + int ___ = 3*_; + int _______ = 7*_; + const int codes[] = { + dit,_,dit,_,dit,___,dah,_,dah,_,dah,___,dit,_,dit,_,dit,_______ + }; + size_t s = 0; + struct input_event e; + bool done = false; + init_debug_led(); + enable_debug_led(); + do { + int timeout = abs(codes[s]) * ms; + int res = get_event(&e, timeout); + if (res == 0) { + if (e.type == EV_KEY + && (e.code == KEY_HOME || e.code == KEY_VOLUMEDOWN) + && e.value == 0) { + done = true; + } + } else if (res == 1) { + if (++s >= sizeof(codes)/sizeof(*codes)) + s = 0; + if (codes[s] > 0) { + enable_debug_led(); + } else { + disable_debug_led(); + } + } + } while (!done); + uninit_getevent(); + } + + // don't forget to turn debug led off + disable_debug_led(); + LOG("debuggerd resuming process %d", pid); +} + +static int get_process_info(pid_t tid, pid_t* out_pid, uid_t* out_uid, uid_t* out_gid) { + char path[64]; + snprintf(path, sizeof(path), "/proc/%d/status", tid); + + FILE* fp = fopen(path, "r"); + if (!fp) { + return -1; + } + + int fields = 0; + char line[1024]; + while (fgets(line, sizeof(line), fp)) { + size_t len = strlen(line); + if (len > 6 && !memcmp(line, "Tgid:\t", 6)) { + *out_pid = atoi(line + 6); + fields |= 1; + } else if (len > 5 && !memcmp(line, "Uid:\t", 5)) { + *out_uid = atoi(line + 5); + fields |= 2; + } else if (len > 5 && !memcmp(line, "Gid:\t", 5)) { + *out_gid = atoi(line + 5); + fields |= 4; + } + } + fclose(fp); + return fields == 7 ? 0 : -1; +} + +static int read_request(int fd, debugger_request_t* out_request) { + struct ucred cr; + int len = sizeof(cr); + int status = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len); + if (status != 0) { + LOG("cannot get credentials\n"); + return -1; + } + + XLOG("reading tid\n"); + fcntl(fd, F_SETFL, O_NONBLOCK); + + struct pollfd pollfds[1]; + pollfds[0].fd = fd; + pollfds[0].events = POLLIN; + pollfds[0].revents = 0; + status = TEMP_FAILURE_RETRY(poll(pollfds, 1, 3000)); + if (status != 1) { + LOG("timed out reading tid (from pid=%d uid=%d)\n", cr.pid, cr.uid); + return -1; + } + + debugger_msg_t msg; + memset(&msg, 0, sizeof(msg)); + status = TEMP_FAILURE_RETRY(read(fd, &msg, sizeof(msg))); + if (status < 0) { + LOG("read failure? %s (pid=%d uid=%d)\n", strerror(errno), cr.pid, cr.uid); + return -1; + } + if (status == sizeof(debugger_msg_t)) { + XLOG("crash request of size %d abort_msg_address=%#08x\n", status, msg.abort_msg_address); + } else { + LOG("invalid crash request of size %d (from pid=%d uid=%d)\n", status, cr.pid, cr.uid); + return -1; + } + + out_request->action = msg.action; + out_request->tid = msg.tid; + out_request->pid = cr.pid; + out_request->uid = cr.uid; + out_request->gid = cr.gid; + out_request->abort_msg_address = msg.abort_msg_address; + + if (msg.action == DEBUGGER_ACTION_CRASH) { + // Ensure that the tid reported by the crashing process is valid. + char buf[64]; + struct stat s; + snprintf(buf, sizeof buf, "/proc/%d/task/%d", out_request->pid, out_request->tid); + if (stat(buf, &s)) { + LOG("tid %d does not exist in pid %d. ignoring debug request\n", + out_request->tid, out_request->pid); + return -1; + } + } else if (cr.uid == 0 + || (cr.uid == AID_SYSTEM && msg.action == DEBUGGER_ACTION_DUMP_BACKTRACE)) { + // Only root or system can ask us to attach to any process and dump it explicitly. + // However, system is only allowed to collect backtraces but cannot dump tombstones. + status = get_process_info(out_request->tid, &out_request->pid, + &out_request->uid, &out_request->gid); + if (status < 0) { + LOG("tid %d does not exist. ignoring explicit dump request\n", out_request->tid); + return -1; + } + } else { + // No one else is allowed to dump arbitrary processes. + return -1; + } + return 0; +} + +static bool should_attach_gdb(debugger_request_t* request) { + if (request->action == DEBUGGER_ACTION_CRASH) { + char value[PROPERTY_VALUE_MAX]; + property_get("debug.db.uid", value, "-1"); + int debug_uid = atoi(value); + return debug_uid >= 0 && request->uid <= (uid_t)debug_uid; + } + return false; +} + +static void handle_request(int fd) { + XLOG("handle_request(%d)\n", fd); + + debugger_request_t request; + memset(&request, 0, sizeof(request)); + int status = read_request(fd, &request); + if (!status) { + XLOG("BOOM: pid=%d uid=%d gid=%d tid=%d\n", + request.pid, request.uid, request.gid, request.tid); + + // At this point, the thread that made the request is blocked in + // a read() call. If the thread has crashed, then this gives us + // time to PTRACE_ATTACH to it before it has a chance to really fault. + // + // The PTRACE_ATTACH sends a SIGSTOP to the target process, but it + // won't necessarily have stopped by the time ptrace() returns. (We + // currently assume it does.) We write to the file descriptor to + // ensure that it can run as soon as we call PTRACE_CONT below. + // See details in bionic/libc/linker/debugger.c, in function + // debugger_signal_handler(). + if (ptrace(PTRACE_ATTACH, request.tid, 0, 0)) { + LOG("ptrace attach failed: %s\n", strerror(errno)); + } else { + bool detach_failed = false; + bool attach_gdb = should_attach_gdb(&request); + if (TEMP_FAILURE_RETRY(write(fd, "\0", 1)) != 1) { + LOG("failed responding to client: %s\n", strerror(errno)); + } else { + char* tombstone_path = NULL; + + if (request.action == DEBUGGER_ACTION_CRASH) { + close(fd); + fd = -1; + } + + int total_sleep_time_usec = 0; + for (;;) { + int signal = wait_for_signal(request.tid, &total_sleep_time_usec); + if (signal < 0) { + break; + } + + switch (signal) { + case SIGSTOP: + if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) { + XLOG("stopped -- dumping to tombstone\n"); + tombstone_path = engrave_tombstone( + request.pid, request.tid, signal, request.abort_msg_address, true, true, + &detach_failed, &total_sleep_time_usec); + } else if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE) { + XLOG("stopped -- dumping to fd\n"); + dump_backtrace(fd, -1, request.pid, request.tid, &detach_failed, + &total_sleep_time_usec); + } else { + XLOG("stopped -- continuing\n"); + status = ptrace(PTRACE_CONT, request.tid, 0, 0); + if (status) { + LOG("ptrace continue failed: %s\n", strerror(errno)); + } + continue; // loop again + } + break; + + case SIGILL: + case SIGABRT: + case SIGBUS: + case SIGFPE: + case SIGSEGV: + case SIGPIPE: +#ifdef SIGSTKFLT + case SIGSTKFLT: +#endif + XLOG("stopped -- fatal signal\n"); + // Send a SIGSTOP to the process to make all of + // the non-signaled threads stop moving. Without + // this we get a lot of "ptrace detach failed: + // No such process". + kill(request.pid, SIGSTOP); + // don't dump sibling threads when attaching to GDB because it + // makes the process less reliable, apparently... + tombstone_path = engrave_tombstone( + request.pid, request.tid, signal, request.abort_msg_address, !attach_gdb, + false, &detach_failed, &total_sleep_time_usec); + break; + + default: + XLOG("stopped -- unexpected signal\n"); + LOG("process stopped due to unexpected signal %d\n", signal); + break; + } + break; + } + + if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) { + if (tombstone_path) { + write(fd, tombstone_path, strlen(tombstone_path)); + } + close(fd); + fd = -1; + } + free(tombstone_path); + } + + XLOG("detaching\n"); + if (attach_gdb) { + // stop the process so we can debug + kill(request.pid, SIGSTOP); + + // detach so we can attach gdbserver + if (ptrace(PTRACE_DETACH, request.tid, 0, 0)) { + LOG("ptrace detach from %d failed: %s\n", request.tid, strerror(errno)); + detach_failed = true; + } + + // if debug.db.uid is set, its value indicates if we should wait + // for user action for the crashing process. + // in this case, we log a message and turn the debug LED on + // waiting for a gdb connection (for instance) + wait_for_user_action(request.pid); + } else { + // just detach + if (ptrace(PTRACE_DETACH, request.tid, 0, 0)) { + LOG("ptrace detach from %d failed: %s\n", request.tid, strerror(errno)); + detach_failed = true; + } + } + + // resume stopped process (so it can crash in peace). + kill(request.pid, SIGCONT); + + // If we didn't successfully detach, we're still the parent, and the + // actual parent won't receive a death notification via wait(2). At this point + // there's not much we can do about that. + if (detach_failed) { + LOG("debuggerd committing suicide to free the zombie!\n"); + kill(getpid(), SIGKILL); + } + } + + } + if (fd >= 0) { + close(fd); + } +} + +static int do_server() { + int s; + struct sigaction act; + int logsocket = -1; + + // debuggerd crashes can't be reported to debuggerd. Reset all of the + // crash handlers. + signal(SIGILL, SIG_DFL); + signal(SIGABRT, SIG_DFL); + signal(SIGBUS, SIG_DFL); + signal(SIGFPE, SIG_DFL); + signal(SIGSEGV, SIG_DFL); +#ifdef SIGSTKFLT + signal(SIGSTKFLT, SIG_DFL); +#endif + + // Ignore failed writes to closed sockets + signal(SIGPIPE, SIG_IGN); + + logsocket = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_DGRAM); + if (logsocket < 0) { + logsocket = -1; + } else { + fcntl(logsocket, F_SETFD, FD_CLOEXEC); + } + + act.sa_handler = SIG_DFL; + sigemptyset(&act.sa_mask); + sigaddset(&act.sa_mask,SIGCHLD); + act.sa_flags = SA_NOCLDWAIT; + sigaction(SIGCHLD, &act, 0); + + s = socket_local_server(DEBUGGER_SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); + if (s < 0) + return 1; + fcntl(s, F_SETFD, FD_CLOEXEC); + + LOG("debuggerd: " __DATE__ " " __TIME__ "\n"); + + for (;;) { + struct sockaddr addr; + socklen_t alen; + int fd; + + alen = sizeof(addr); + XLOG("waiting for connection\n"); + fd = accept(s, &addr, &alen); + if (fd < 0) { + XLOG("accept failed: %s\n", strerror(errno)); + continue; + } + + fcntl(fd, F_SETFD, FD_CLOEXEC); + + handle_request(fd); + } + return 0; +} + +static int do_explicit_dump(pid_t tid, bool dump_backtrace) { + fprintf(stdout, "Sending request to dump task %d.\n", tid); + + if (dump_backtrace) { + fflush(stdout); + if (dump_backtrace_to_file(tid, fileno(stdout)) < 0) { + fputs("Error dumping backtrace.\n", stderr); + return 1; + } + } else { + char tombstone_path[PATH_MAX]; + if (dump_tombstone(tid, tombstone_path, sizeof(tombstone_path)) < 0) { + fputs("Error dumping tombstone.\n", stderr); + return 1; + } + fprintf(stderr, "Tombstone written to: %s\n", tombstone_path); + } + return 0; +} + +static void usage() { + fputs("Usage: -b [<tid>]\n" + " -b dump backtrace to console, otherwise dump full tombstone file\n" + "\n" + "If tid specified, sends a request to debuggerd to dump that task.\n" + "Otherwise, starts the debuggerd server.\n", stderr); +} + +int main(int argc, char** argv) { + if (argc == 1) { + return do_server(); + } + + bool dump_backtrace = false; + bool have_tid = false; + pid_t tid = 0; + for (int i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-b")) { + dump_backtrace = true; + } else if (!have_tid) { + tid = atoi(argv[i]); + have_tid = true; + } else { + usage(); + return 1; + } + } + if (!have_tid) { + usage(); + return 1; + } + return do_explicit_dump(tid, dump_backtrace); +} diff --git a/debuggerd/getevent.c b/debuggerd/getevent.c deleted file mode 100644 index ebd070c..0000000 --- a/debuggerd/getevent.c +++ /dev/null @@ -1,219 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <stdint.h> -#include <dirent.h> -#include <fcntl.h> -#include <sys/ioctl.h> -#include <sys/inotify.h> -#include <sys/limits.h> -#include <sys/poll.h> -#include <linux/input.h> -#include <errno.h> -#include <cutils/log.h> - -static struct pollfd *ufds; -static char **device_names; -static int nfds; - -static int open_device(const char *device) -{ - int version; - int fd; - struct pollfd *new_ufds; - char **new_device_names; - char name[80]; - char location[80]; - char idstr[80]; - struct input_id id; - - fd = open(device, O_RDWR); - if(fd < 0) { - return -1; - } - - if(ioctl(fd, EVIOCGVERSION, &version)) { - return -1; - } - if(ioctl(fd, EVIOCGID, &id)) { - return -1; - } - name[sizeof(name) - 1] = '\0'; - location[sizeof(location) - 1] = '\0'; - idstr[sizeof(idstr) - 1] = '\0'; - if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) { - //fprintf(stderr, "could not get device name for %s, %s\n", device, strerror(errno)); - name[0] = '\0'; - } - if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) { - //fprintf(stderr, "could not get location for %s, %s\n", device, strerror(errno)); - location[0] = '\0'; - } - if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) { - //fprintf(stderr, "could not get idstring for %s, %s\n", device, strerror(errno)); - idstr[0] = '\0'; - } - - new_ufds = realloc(ufds, sizeof(ufds[0]) * (nfds + 1)); - if(new_ufds == NULL) { - fprintf(stderr, "out of memory\n"); - return -1; - } - ufds = new_ufds; - new_device_names = realloc(device_names, sizeof(device_names[0]) * (nfds + 1)); - if(new_device_names == NULL) { - fprintf(stderr, "out of memory\n"); - return -1; - } - device_names = new_device_names; - ufds[nfds].fd = fd; - ufds[nfds].events = POLLIN; - device_names[nfds] = strdup(device); - nfds++; - - return 0; -} - -int close_device(const char *device) -{ - int i; - for(i = 1; i < nfds; i++) { - if(strcmp(device_names[i], device) == 0) { - int count = nfds - i - 1; - free(device_names[i]); - memmove(device_names + i, device_names + i + 1, sizeof(device_names[0]) * count); - memmove(ufds + i, ufds + i + 1, sizeof(ufds[0]) * count); - nfds--; - return 0; - } - } - return -1; -} - -static int read_notify(const char *dirname, int nfd) -{ - int res; - char devname[PATH_MAX]; - char *filename; - char event_buf[512]; - int event_size; - int event_pos = 0; - struct inotify_event *event; - - res = read(nfd, event_buf, sizeof(event_buf)); - if(res < (int)sizeof(*event)) { - if(errno == EINTR) - return 0; - fprintf(stderr, "could not get event, %s\n", strerror(errno)); - return 1; - } - //printf("got %d bytes of event information\n", res); - - strcpy(devname, dirname); - filename = devname + strlen(devname); - *filename++ = '/'; - - while(res >= (int)sizeof(*event)) { - event = (struct inotify_event *)(event_buf + event_pos); - //printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : ""); - if(event->len) { - strcpy(filename, event->name); - if(event->mask & IN_CREATE) { - open_device(devname); - } - else { - close_device(devname); - } - } - event_size = sizeof(*event) + event->len; - res -= event_size; - event_pos += event_size; - } - return 0; -} - -static int scan_dir(const char *dirname) -{ - char devname[PATH_MAX]; - char *filename; - DIR *dir; - struct dirent *de; - dir = opendir(dirname); - if(dir == NULL) - return -1; - strcpy(devname, dirname); - filename = devname + strlen(devname); - *filename++ = '/'; - while((de = readdir(dir))) { - if(de->d_name[0] == '.' && - (de->d_name[1] == '\0' || - (de->d_name[1] == '.' && de->d_name[2] == '\0'))) - continue; - strcpy(filename, de->d_name); - open_device(devname); - } - closedir(dir); - return 0; -} - -int init_getevent() -{ - int res; - const char *device_path = "/dev/input"; - - nfds = 1; - ufds = calloc(1, sizeof(ufds[0])); - ufds[0].fd = inotify_init(); - ufds[0].events = POLLIN; - - res = inotify_add_watch(ufds[0].fd, device_path, IN_DELETE | IN_CREATE); - if(res < 0) { - return 1; - } - res = scan_dir(device_path); - if(res < 0) { - return 1; - } - return 0; -} - -void uninit_getevent() -{ - int i; - for(i = 0; i < nfds; i++) { - close(ufds[i].fd); - } - free(ufds); - ufds = 0; - nfds = 0; -} - -int get_event(struct input_event* event, int timeout) -{ - int res; - int i; - int pollres; - const char *device_path = "/dev/input"; - while(1) { - pollres = poll(ufds, nfds, timeout); - if (pollres == 0) { - return 1; - } - if(ufds[0].revents & POLLIN) { - read_notify(device_path, ufds[0].fd); - } - for(i = 1; i < nfds; i++) { - if(ufds[i].revents) { - if(ufds[i].revents & POLLIN) { - res = read(ufds[i].fd, event, sizeof(*event)); - if(res < (int)sizeof(event)) { - fprintf(stderr, "could not get event\n"); - return -1; - } - return 0; - } - } - } - } - return 0; -} diff --git a/debuggerd/getevent.cpp b/debuggerd/getevent.cpp new file mode 100644 index 0000000..751c4fb --- /dev/null +++ b/debuggerd/getevent.cpp @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2014 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 <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <dirent.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/inotify.h> +#include <sys/limits.h> +#include <sys/poll.h> +#include <linux/input.h> +#include <errno.h> +#include <cutils/log.h> + +static struct pollfd* ufds; +static char** device_names; +static int nfds; + +static int open_device(const char* device) { + int version; + int fd; + struct pollfd* new_ufds; + char** new_device_names; + char name[80]; + char location[80]; + char idstr[80]; + struct input_id id; + + fd = open(device, O_RDWR); + if (fd < 0) { + return -1; + } + + if (ioctl(fd, EVIOCGVERSION, &version)) { + return -1; + } + if (ioctl(fd, EVIOCGID, &id)) { + return -1; + } + name[sizeof(name) - 1] = '\0'; + location[sizeof(location) - 1] = '\0'; + idstr[sizeof(idstr) - 1] = '\0'; + if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) { + name[0] = '\0'; + } + if (ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) { + location[0] = '\0'; + } + if (ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) { + idstr[0] = '\0'; + } + + new_ufds = reinterpret_cast<pollfd*>(realloc(ufds, sizeof(ufds[0]) * (nfds + 1))); + if (new_ufds == NULL) { + fprintf(stderr, "out of memory\n"); + return -1; + } + ufds = new_ufds; + new_device_names = reinterpret_cast<char**>(realloc( + device_names, sizeof(device_names[0]) * (nfds + 1))); + if (new_device_names == NULL) { + fprintf(stderr, "out of memory\n"); + return -1; + } + device_names = new_device_names; + ufds[nfds].fd = fd; + ufds[nfds].events = POLLIN; + device_names[nfds] = strdup(device); + nfds++; + + return 0; +} + +int close_device(const char* device) { + int i; + for (i = 1; i < nfds; i++) { + if (strcmp(device_names[i], device) == 0) { + int count = nfds - i - 1; + free(device_names[i]); + memmove(device_names + i, device_names + i + 1, sizeof(device_names[0]) * count); + memmove(ufds + i, ufds + i + 1, sizeof(ufds[0]) * count); + nfds--; + return 0; + } + } + return -1; +} + +static int read_notify(const char* dirname, int nfd) { + int res; + char devname[PATH_MAX]; + char* filename; + char event_buf[512]; + int event_size; + int event_pos = 0; + struct inotify_event *event; + + res = read(nfd, event_buf, sizeof(event_buf)); + if (res < (int)sizeof(*event)) { + if (errno == EINTR) + return 0; + fprintf(stderr, "could not get event, %s\n", strerror(errno)); + return 1; + } + + strcpy(devname, dirname); + filename = devname + strlen(devname); + *filename++ = '/'; + + while (res >= (int)sizeof(*event)) { + event = reinterpret_cast<struct inotify_event*>(event_buf + event_pos); + if (event->len) { + strcpy(filename, event->name); + if (event->mask & IN_CREATE) { + open_device(devname); + } else { + close_device(devname); + } + } + event_size = sizeof(*event) + event->len; + res -= event_size; + event_pos += event_size; + } + return 0; +} + +static int scan_dir(const char* dirname) { + char devname[PATH_MAX]; + char* filename; + DIR* dir; + struct dirent* de; + dir = opendir(dirname); + if (dir == NULL) + return -1; + strcpy(devname, dirname); + filename = devname + strlen(devname); + *filename++ = '/'; + while ((de = readdir(dir))) { + if ((de->d_name[0] == '.' && de->d_name[1] == '\0') || + (de->d_name[1] == '.' && de->d_name[2] == '\0')) + continue; + strcpy(filename, de->d_name); + open_device(devname); + } + closedir(dir); + return 0; +} + +int init_getevent() { + int res; + const char* device_path = "/dev/input"; + + nfds = 1; + ufds = reinterpret_cast<pollfd*>(calloc(1, sizeof(ufds[0]))); + ufds[0].fd = inotify_init(); + ufds[0].events = POLLIN; + + res = inotify_add_watch(ufds[0].fd, device_path, IN_DELETE | IN_CREATE); + if (res < 0) { + return 1; + } + res = scan_dir(device_path); + if (res < 0) { + return 1; + } + return 0; +} + +void uninit_getevent() { + int i; + for (i = 0; i < nfds; i++) { + close(ufds[i].fd); + } + free(ufds); + ufds = 0; + nfds = 0; +} + +int get_event(struct input_event* event, int timeout) { + int res; + int i; + int pollres; + const char* device_path = "/dev/input"; + while (1) { + pollres = poll(ufds, nfds, timeout); + if (pollres == 0) { + return 1; + } + if (ufds[0].revents & POLLIN) { + read_notify(device_path, ufds[0].fd); + } + for (i = 1; i < nfds; i++) { + if (ufds[i].revents) { + if (ufds[i].revents & POLLIN) { + res = read(ufds[i].fd, event, sizeof(*event)); + if (res < static_cast<int>(sizeof(event))) { + fprintf(stderr, "could not get event\n"); + return -1; + } + return 0; + } + } + } + } + return 0; +} diff --git a/debuggerd/machine.h b/debuggerd/machine.h index 1619dd3..2f1e201 100644 --- a/debuggerd/machine.h +++ b/debuggerd/machine.h @@ -17,15 +17,11 @@ #ifndef _DEBUGGERD_MACHINE_H #define _DEBUGGERD_MACHINE_H -#include <stddef.h> -#include <stdbool.h> #include <sys/types.h> -#include <corkscrew/ptrace.h> - #include "utility.h" -void dump_memory_and_code(const ptrace_context_t* context, log_t* log, pid_t tid, bool at_fault); -void dump_registers(const ptrace_context_t* context, log_t* log, pid_t tid, bool at_fault); +void dump_memory_and_code(log_t* log, pid_t tid, int scope_flags); +void dump_registers(log_t* log, pid_t tid, int scope_flags); #endif // _DEBUGGERD_MACHINE_H diff --git a/debuggerd/mips/machine.c b/debuggerd/mips/machine.c deleted file mode 100644 index 65fdf02..0000000 --- a/debuggerd/mips/machine.c +++ /dev/null @@ -1,179 +0,0 @@ -/* system/debuggerd/debuggerd.c -** -** 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. -*/ - -#include <stddef.h> -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> -#include <stdio.h> -#include <errno.h> -#include <sys/types.h> -#include <sys/ptrace.h> - -#include <corkscrew/ptrace.h> - -#include <linux/user.h> - -#include "../utility.h" -#include "../machine.h" - -/* enable to dump memory pointed to by every register */ -#define DUMP_MEMORY_FOR_ALL_REGISTERS 1 - -#define R(x) ((unsigned int)(x)) - -static void dump_memory(log_t* log, pid_t tid, uintptr_t addr, int scopeFlags) { - char code_buffer[64]; /* actual 8+1+((8+1)*4) + 1 == 45 */ - char ascii_buffer[32]; /* actual 16 + 1 == 17 */ - uintptr_t p, end; - - p = addr & ~3; - p -= 32; - if (p > addr) { - /* catch underflow */ - p = 0; - } - end = p + 80; - /* catch overflow; 'end - p' has to be multiples of 16 */ - while (end < p) - end -= 16; - - /* Dump the code around PC as: - * addr contents ascii - * 00008d34 ef000000 e8bd0090 e1b00000 512fff1e ............../Q - * 00008d44 ea00b1f9 e92d0090 e3a070fc ef000000 ......-..p...... - */ - while (p < end) { - char* asc_out = ascii_buffer; - - sprintf(code_buffer, "%08x ", p); - - int i; - for (i = 0; i < 4; i++) { - /* - * If we see (data == -1 && errno != 0), we know that the ptrace - * call failed, probably because we're dumping memory in an - * unmapped or inaccessible page. I don't know if there's - * value in making that explicit in the output -- it likely - * just complicates parsing and clarifies nothing for the - * enlightened reader. - */ - long data = ptrace(PTRACE_PEEKTEXT, tid, (void*)p, NULL); - sprintf(code_buffer + strlen(code_buffer), "%08lx ", data); - - int j; - for (j = 0; j < 4; j++) { - /* - * Our isprint() allows high-ASCII characters that display - * differently (often badly) in different viewers, so we - * just use a simpler test. - */ - char val = (data >> (j*8)) & 0xff; - if (val >= 0x20 && val < 0x7f) { - *asc_out++ = val; - } else { - *asc_out++ = '.'; - } - } - p += 4; - } - *asc_out = '\0'; - _LOG(log, scopeFlags, " %s %s\n", code_buffer, ascii_buffer); - } -} - -/* - * If configured to do so, dump memory around *all* registers - * for the crashing thread. - */ -void dump_memory_and_code(const ptrace_context_t* context __attribute((unused)), - log_t* log, pid_t tid, bool at_fault) { - pt_regs_mips_t r; - if(ptrace(PTRACE_GETREGS, tid, 0, &r)) { - return; - } - - int scopeFlags = at_fault ? SCOPE_AT_FAULT : 0; - if (at_fault && DUMP_MEMORY_FOR_ALL_REGISTERS) { - static const char REG_NAMES[] = "$0atv0v1a0a1a2a3t0t1t2t3t4t5t6t7s0s1s2s3s4s5s6s7t8t9k0k1gpsps8ra"; - - for (int reg = 0; reg < 32; reg++) { - /* skip uninteresting registers */ - if (reg == 0 /* $0 */ - || reg == 26 /* $k0 */ - || reg == 27 /* $k1 */ - || reg == 31 /* $ra (done below) */ - ) - continue; - - uintptr_t addr = R(r.regs[reg]); - - /* - * Don't bother if it looks like a small int or ~= null, or if - * it's in the kernel area. - */ - if (addr < 4096 || addr >= 0x80000000) { - continue; - } - - _LOG(log, scopeFlags | SCOPE_SENSITIVE, "\nmemory near %.2s:\n", ®_NAMES[reg * 2]); - dump_memory(log, tid, addr, scopeFlags | SCOPE_SENSITIVE); - } - } - - unsigned int pc = R(r.cp0_epc); - unsigned int ra = R(r.regs[31]); - - _LOG(log, scopeFlags, "\ncode around pc:\n"); - dump_memory(log, tid, (uintptr_t)pc, scopeFlags); - - if (pc != ra) { - _LOG(log, scopeFlags, "\ncode around ra:\n"); - dump_memory(log, tid, (uintptr_t)ra, scopeFlags); - } -} - -void dump_registers(const ptrace_context_t* context __attribute((unused)), - log_t* log, pid_t tid, bool at_fault) -{ - pt_regs_mips_t r; - int scopeFlags = at_fault ? SCOPE_AT_FAULT : 0; - - if(ptrace(PTRACE_GETREGS, tid, 0, &r)) { - _LOG(log, scopeFlags, "cannot get registers: %s\n", strerror(errno)); - return; - } - - _LOG(log, scopeFlags, " zr %08x at %08x v0 %08x v1 %08x\n", - R(r.regs[0]), R(r.regs[1]), R(r.regs[2]), R(r.regs[3])); - _LOG(log, scopeFlags, " a0 %08x a1 %08x a2 %08x a3 %08x\n", - R(r.regs[4]), R(r.regs[5]), R(r.regs[6]), R(r.regs[7])); - _LOG(log, scopeFlags, " t0 %08x t1 %08x t2 %08x t3 %08x\n", - R(r.regs[8]), R(r.regs[9]), R(r.regs[10]), R(r.regs[11])); - _LOG(log, scopeFlags, " t4 %08x t5 %08x t6 %08x t7 %08x\n", - R(r.regs[12]), R(r.regs[13]), R(r.regs[14]), R(r.regs[15])); - _LOG(log, scopeFlags, " s0 %08x s1 %08x s2 %08x s3 %08x\n", - R(r.regs[16]), R(r.regs[17]), R(r.regs[18]), R(r.regs[19])); - _LOG(log, scopeFlags, " s4 %08x s5 %08x s6 %08x s7 %08x\n", - R(r.regs[20]), R(r.regs[21]), R(r.regs[22]), R(r.regs[23])); - _LOG(log, scopeFlags, " t8 %08x t9 %08x k0 %08x k1 %08x\n", - R(r.regs[24]), R(r.regs[25]), R(r.regs[26]), R(r.regs[27])); - _LOG(log, scopeFlags, " gp %08x sp %08x s8 %08x ra %08x\n", - R(r.regs[28]), R(r.regs[29]), R(r.regs[30]), R(r.regs[31])); - _LOG(log, scopeFlags, " hi %08x lo %08x bva %08x epc %08x\n", - R(r.hi), R(r.lo), R(r.cp0_badvaddr), R(r.cp0_epc)); -} diff --git a/debuggerd/mips/machine.cpp b/debuggerd/mips/machine.cpp new file mode 100644 index 0000000..d1a7f2d --- /dev/null +++ b/debuggerd/mips/machine.cpp @@ -0,0 +1,162 @@ +/* + * 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. + */ + +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/ptrace.h> + +#include <corkscrew/ptrace.h> + +#include <sys/user.h> + +#include "../utility.h" +#include "../machine.h" + +// enable to dump memory pointed to by every register +#define DUMP_MEMORY_FOR_ALL_REGISTERS 1 + +#define R(x) (static_cast<unsigned int>(x)) + +static void dump_memory(log_t* log, pid_t tid, uintptr_t addr, int scope_flags) { + char code_buffer[64]; // actual 8+1+((8+1)*4) + 1 == 45 + char ascii_buffer[32]; // actual 16 + 1 == 17 + uintptr_t p, end; + + p = addr & ~3; + p -= 32; + if (p > addr) { + // catch underflow + p = 0; + } + end = p + 80; + // catch overflow; 'end - p' has to be multiples of 16 + while (end < p) + end -= 16; + + // Dump the code around PC as: + // addr contents ascii + // 00008d34 ef000000 e8bd0090 e1b00000 512fff1e ............../Q + // 00008d44 ea00b1f9 e92d0090 e3a070fc ef000000 ......-..p...... + while (p < end) { + char* asc_out = ascii_buffer; + + sprintf(code_buffer, "%08x ", p); + + int i; + for (i = 0; i < 4; i++) { + // If we see (data == -1 && errno != 0), we know that the ptrace + // call failed, probably because we're dumping memory in an + // unmapped or inaccessible page. I don't know if there's + // value in making that explicit in the output -- it likely + // just complicates parsing and clarifies nothing for the + // enlightened reader. + long data = ptrace(PTRACE_PEEKTEXT, tid, (void*)p, NULL); + sprintf(code_buffer + strlen(code_buffer), "%08lx ", data); + + int j; + for (j = 0; j < 4; j++) { + // Our isprint() allows high-ASCII characters that display + // differently (often badly) in different viewers, so we + // just use a simpler test. + char val = (data >> (j*8)) & 0xff; + if (val >= 0x20 && val < 0x7f) { + *asc_out++ = val; + } else { + *asc_out++ = '.'; + } + } + p += 4; + } + *asc_out = '\0'; + _LOG(log, scope_flags, " %s %s\n", code_buffer, ascii_buffer); + } +} + +// If configured to do so, dump memory around *all* registers +// for the crashing thread. +void dump_memory_and_code(log_t* log, pid_t tid, int scope_flags) { + pt_regs_mips_t r; + if (ptrace(PTRACE_GETREGS, tid, 0, &r)) { + return; + } + + if (IS_AT_FAULT(scope_flags) && DUMP_MEMORY_FOR_ALL_REGISTERS) { + static const char REG_NAMES[] = "$0atv0v1a0a1a2a3t0t1t2t3t4t5t6t7s0s1s2s3s4s5s6s7t8t9k0k1gpsps8ra"; + + for (int reg = 0; reg < 32; reg++) { + // skip uninteresting registers + if (reg == 0 // $0 + || reg == 26 // $k0 + || reg == 27 // $k1 + || reg == 31 // $ra (done below) + ) + continue; + + uintptr_t addr = R(r.regs[reg]); + + // Don't bother if it looks like a small int or ~= null, or if + // it's in the kernel area. + if (addr < 4096 || addr >= 0x80000000) { + continue; + } + + _LOG(log, scope_flags | SCOPE_SENSITIVE, "\nmemory near %.2s:\n", ®_NAMES[reg * 2]); + dump_memory(log, tid, addr, scope_flags | SCOPE_SENSITIVE); + } + } + + unsigned int pc = R(r.cp0_epc); + unsigned int ra = R(r.regs[31]); + + _LOG(log, scope_flags, "\ncode around pc:\n"); + dump_memory(log, tid, (uintptr_t)pc, scope_flags); + + if (pc != ra) { + _LOG(log, scope_flags, "\ncode around ra:\n"); + dump_memory(log, tid, (uintptr_t)ra, scope_flags); + } +} + +void dump_registers(log_t* log, pid_t tid, int scope_flags) { + pt_regs_mips_t r; + if(ptrace(PTRACE_GETREGS, tid, 0, &r)) { + _LOG(log, scope_flags, "cannot get registers: %s\n", strerror(errno)); + return; + } + + _LOG(log, scope_flags, " zr %08x at %08x v0 %08x v1 %08x\n", + R(r.regs[0]), R(r.regs[1]), R(r.regs[2]), R(r.regs[3])); + _LOG(log, scope_flags, " a0 %08x a1 %08x a2 %08x a3 %08x\n", + R(r.regs[4]), R(r.regs[5]), R(r.regs[6]), R(r.regs[7])); + _LOG(log, scope_flags, " t0 %08x t1 %08x t2 %08x t3 %08x\n", + R(r.regs[8]), R(r.regs[9]), R(r.regs[10]), R(r.regs[11])); + _LOG(log, scope_flags, " t4 %08x t5 %08x t6 %08x t7 %08x\n", + R(r.regs[12]), R(r.regs[13]), R(r.regs[14]), R(r.regs[15])); + _LOG(log, scope_flags, " s0 %08x s1 %08x s2 %08x s3 %08x\n", + R(r.regs[16]), R(r.regs[17]), R(r.regs[18]), R(r.regs[19])); + _LOG(log, scope_flags, " s4 %08x s5 %08x s6 %08x s7 %08x\n", + R(r.regs[20]), R(r.regs[21]), R(r.regs[22]), R(r.regs[23])); + _LOG(log, scope_flags, " t8 %08x t9 %08x k0 %08x k1 %08x\n", + R(r.regs[24]), R(r.regs[25]), R(r.regs[26]), R(r.regs[27])); + _LOG(log, scope_flags, " gp %08x sp %08x s8 %08x ra %08x\n", + R(r.regs[28]), R(r.regs[29]), R(r.regs[30]), R(r.regs[31])); + _LOG(log, scope_flags, " hi %08x lo %08x bva %08x epc %08x\n", + R(r.hi), R(r.lo), R(r.cp0_badvaddr), R(r.cp0_epc)); +} diff --git a/debuggerd/tombstone.c b/debuggerd/tombstone.c deleted file mode 100644 index 7009a8e..0000000 --- a/debuggerd/tombstone.c +++ /dev/null @@ -1,838 +0,0 @@ -/* - * 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 <stddef.h> -#include <stdbool.h> -#include <stdlib.h> -#include <signal.h> -#include <string.h> -#include <stdio.h> -#include <fcntl.h> -#include <errno.h> -#include <dirent.h> -#include <time.h> -#include <sys/ptrace.h> -#include <sys/stat.h> - -#include <private/android_filesystem_config.h> - -#include <log/logger.h> -#include <cutils/properties.h> - -#include <corkscrew/demangle.h> -#include <corkscrew/backtrace.h> - -#include <sys/socket.h> -#include <linux/un.h> - -#include <selinux/android.h> - -#include "machine.h" -#include "tombstone.h" -#include "utility.h" - -#define STACK_DEPTH 32 -#define STACK_WORDS 16 - -#define MAX_TOMBSTONES 10 -#define TOMBSTONE_DIR "/data/tombstones" - -/* Must match the path defined in NativeCrashListener.java */ -#define NCRASH_SOCKET_PATH "/data/system/ndebugsocket" - -#define typecheck(x,y) { \ - typeof(x) __dummy1; \ - typeof(y) __dummy2; \ - (void)(&__dummy1 == &__dummy2); } - - -static bool signal_has_address(int sig) { - switch (sig) { - case SIGILL: - case SIGFPE: - case SIGSEGV: - case SIGBUS: - return true; - default: - return false; - } -} - -static const char *get_signame(int sig) -{ - switch(sig) { - case SIGILL: return "SIGILL"; - case SIGABRT: return "SIGABRT"; - case SIGBUS: return "SIGBUS"; - case SIGFPE: return "SIGFPE"; - case SIGSEGV: return "SIGSEGV"; - case SIGPIPE: return "SIGPIPE"; -#ifdef SIGSTKFLT - case SIGSTKFLT: return "SIGSTKFLT"; -#endif - case SIGSTOP: return "SIGSTOP"; - default: return "?"; - } -} - -static const char *get_sigcode(int signo, int code) -{ - // Try the signal-specific codes... - switch (signo) { - case SIGILL: - switch (code) { - case ILL_ILLOPC: return "ILL_ILLOPC"; - case ILL_ILLOPN: return "ILL_ILLOPN"; - case ILL_ILLADR: return "ILL_ILLADR"; - case ILL_ILLTRP: return "ILL_ILLTRP"; - case ILL_PRVOPC: return "ILL_PRVOPC"; - case ILL_PRVREG: return "ILL_PRVREG"; - case ILL_COPROC: return "ILL_COPROC"; - case ILL_BADSTK: return "ILL_BADSTK"; - } - break; - case SIGBUS: - switch (code) { - case BUS_ADRALN: return "BUS_ADRALN"; - case BUS_ADRERR: return "BUS_ADRERR"; - case BUS_OBJERR: return "BUS_OBJERR"; - } - break; - case SIGFPE: - switch (code) { - case FPE_INTDIV: return "FPE_INTDIV"; - case FPE_INTOVF: return "FPE_INTOVF"; - case FPE_FLTDIV: return "FPE_FLTDIV"; - case FPE_FLTOVF: return "FPE_FLTOVF"; - case FPE_FLTUND: return "FPE_FLTUND"; - case FPE_FLTRES: return "FPE_FLTRES"; - case FPE_FLTINV: return "FPE_FLTINV"; - case FPE_FLTSUB: return "FPE_FLTSUB"; - } - break; - case SIGSEGV: - switch (code) { - case SEGV_MAPERR: return "SEGV_MAPERR"; - 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, SCOPE_AT_FAULT, "Revision: '%s'\n", revision); -} - -static void dump_build_info(log_t* log) -{ - char fingerprint[PROPERTY_VALUE_MAX]; - - property_get("ro.build.fingerprint", fingerprint, "unknown"); - - _LOG(log, SCOPE_AT_FAULT, "Build fingerprint: '%s'\n", fingerprint); -} - -static void dump_fault_addr(log_t* log, pid_t tid, int sig) -{ - siginfo_t si; - - memset(&si, 0, sizeof(si)); - if(ptrace(PTRACE_GETSIGINFO, tid, 0, &si)){ - _LOG(log, SCOPE_AT_FAULT, "cannot get siginfo: %s\n", strerror(errno)); - } else if (signal_has_address(sig)) { - _LOG(log, SCOPE_AT_FAULT, "signal %d (%s), code %d (%s), fault addr %08x\n", - sig, get_signame(sig), - si.si_code, get_sigcode(sig, si.si_code), - (uintptr_t) si.si_addr); - } else { - _LOG(log, SCOPE_AT_FAULT, "signal %d (%s), code %d (%s), fault addr --------\n", - sig, get_signame(sig), si.si_code, get_sigcode(sig, si.si_code)); - } -} - -static void dump_thread_info(log_t* log, pid_t pid, pid_t tid, bool at_fault) { - char path[64]; - char threadnamebuf[1024]; - char* threadname = NULL; - FILE *fp; - - snprintf(path, sizeof(path), "/proc/%d/comm", tid); - if ((fp = fopen(path, "r"))) { - threadname = fgets(threadnamebuf, sizeof(threadnamebuf), fp); - fclose(fp); - if (threadname) { - size_t len = strlen(threadname); - if (len && threadname[len - 1] == '\n') { - threadname[len - 1] = '\0'; - } - } - } - - if (at_fault) { - char procnamebuf[1024]; - char* procname = NULL; - - snprintf(path, sizeof(path), "/proc/%d/cmdline", pid); - if ((fp = fopen(path, "r"))) { - procname = fgets(procnamebuf, sizeof(procnamebuf), fp); - fclose(fp); - } - - _LOG(log, SCOPE_AT_FAULT, "pid: %d, tid: %d, name: %s >>> %s <<<\n", pid, tid, - threadname ? threadname : "UNKNOWN", - procname ? procname : "UNKNOWN"); - } else { - _LOG(log, 0, "pid: %d, tid: %d, name: %s\n", - pid, tid, threadname ? threadname : "UNKNOWN"); - } -} - -static void dump_backtrace(const ptrace_context_t* context __attribute((unused)), - log_t* log, pid_t tid __attribute((unused)), bool at_fault, - const backtrace_frame_t* backtrace, size_t frames) { - int scopeFlags = at_fault ? SCOPE_AT_FAULT : 0; - _LOG(log, scopeFlags, "\nbacktrace:\n"); - - backtrace_symbol_t backtrace_symbols[STACK_DEPTH]; - get_backtrace_symbols_ptrace(context, backtrace, frames, backtrace_symbols); - for (size_t i = 0; i < frames; i++) { - char line[MAX_BACKTRACE_LINE_LENGTH]; - format_backtrace_line(i, &backtrace[i], &backtrace_symbols[i], - line, MAX_BACKTRACE_LINE_LENGTH); - _LOG(log, scopeFlags, " %s\n", line); - } - free_backtrace_symbols(backtrace_symbols, frames); -} - -static void dump_stack_segment(const ptrace_context_t* context, log_t* log, pid_t tid, - int scopeFlags, uintptr_t* sp, size_t words, int label) { - for (size_t i = 0; i < words; i++) { - uint32_t stack_content; - if (!try_get_word_ptrace(tid, *sp, &stack_content)) { - break; - } - - const map_info_t* mi; - const symbol_t* symbol; - find_symbol_ptrace(context, stack_content, &mi, &symbol); - - if (symbol) { - char* demangled_name = demangle_symbol_name(symbol->name); - const char* symbol_name = demangled_name ? demangled_name : symbol->name; - uint32_t offset = stack_content - (mi->start + symbol->start); - if (!i && label >= 0) { - if (offset) { - _LOG(log, scopeFlags, " #%02d %08x %08x %s (%s+%u)\n", - label, *sp, stack_content, mi ? mi->name : "", symbol_name, offset); - } else { - _LOG(log, scopeFlags, " #%02d %08x %08x %s (%s)\n", - label, *sp, stack_content, mi ? mi->name : "", symbol_name); - } - } else { - if (offset) { - _LOG(log, scopeFlags, " %08x %08x %s (%s+%u)\n", - *sp, stack_content, mi ? mi->name : "", symbol_name, offset); - } else { - _LOG(log, scopeFlags, " %08x %08x %s (%s)\n", - *sp, stack_content, mi ? mi->name : "", symbol_name); - } - } - free(demangled_name); - } else { - if (!i && label >= 0) { - _LOG(log, scopeFlags, " #%02d %08x %08x %s\n", - label, *sp, stack_content, mi ? mi->name : ""); - } else { - _LOG(log, scopeFlags, " %08x %08x %s\n", - *sp, stack_content, mi ? mi->name : ""); - } - } - - *sp += sizeof(uint32_t); - } -} - -static void dump_stack(const ptrace_context_t* context, log_t* log, pid_t tid, bool at_fault, - const backtrace_frame_t* backtrace, size_t frames) { - bool have_first = false; - size_t first, last; - for (size_t i = 0; i < frames; i++) { - if (backtrace[i].stack_top) { - if (!have_first) { - have_first = true; - first = i; - } - last = i; - } - } - if (!have_first) { - return; - } - - int scopeFlags = SCOPE_SENSITIVE | (at_fault ? SCOPE_AT_FAULT : 0); - _LOG(log, scopeFlags, "\nstack:\n"); - - // Dump a few words before the first frame. - uintptr_t sp = backtrace[first].stack_top - STACK_WORDS * sizeof(uint32_t); - dump_stack_segment(context, log, tid, scopeFlags, &sp, STACK_WORDS, -1); - - // Dump a few words from all successive frames. - // Only log the first 3 frames, put the rest in the tombstone. - for (size_t i = first; i <= last; i++) { - const backtrace_frame_t* frame = &backtrace[i]; - if (sp != frame->stack_top) { - _LOG(log, scopeFlags, " ........ ........\n"); - sp = frame->stack_top; - } - if (i - first == 3) { - scopeFlags &= (~SCOPE_AT_FAULT); - } - if (i == last) { - dump_stack_segment(context, log, tid, scopeFlags, &sp, STACK_WORDS, i); - if (sp < frame->stack_top + frame->stack_size) { - _LOG(log, scopeFlags, " ........ ........\n"); - } - } else { - size_t words = frame->stack_size / sizeof(uint32_t); - if (words == 0) { - words = 1; - } else if (words > STACK_WORDS) { - words = STACK_WORDS; - } - dump_stack_segment(context, log, tid, scopeFlags, &sp, words, i); - } - } -} - -static void dump_backtrace_and_stack(const ptrace_context_t* context, log_t* log, pid_t tid, - bool at_fault) { - backtrace_frame_t backtrace[STACK_DEPTH]; - ssize_t frames = unwind_backtrace_ptrace(tid, context, backtrace, 0, STACK_DEPTH); - if (frames > 0) { - dump_backtrace(context, log, tid, at_fault, backtrace, frames); - dump_stack(context, log, tid, at_fault, backtrace, frames); - } -} - -static void dump_map(log_t* log, map_info_t* m, const char* what, int scopeFlags) { - if (m != NULL) { - _LOG(log, scopeFlags, " %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, scopeFlags, " (no %s)\n", what); - } -} - -static void dump_nearby_maps(const ptrace_context_t* context, log_t* log, pid_t tid, bool at_fault) { - int scopeFlags = SCOPE_SENSITIVE | (at_fault ? SCOPE_AT_FAULT : 0); - siginfo_t si; - memset(&si, 0, sizeof(si)); - if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si)) { - _LOG(log, scopeFlags, "cannot get siginfo for %d: %s\n", - tid, strerror(errno)); - return; - } - if (!signal_has_address(si.si_signo)) { - return; - } - - uintptr_t addr = (uintptr_t) si.si_addr; - addr &= ~0xfff; /* round to 4K page boundary */ - if (addr == 0) { /* null-pointer deref */ - return; - } - - _LOG(log, scopeFlags, "\nmemory map around fault addr %08x:\n", (int)si.si_addr); - - /* - * Search for a match, or for a hole where the match would be. The list - * is backward from the file content, so it starts at high addresses. - */ - map_info_t* map = context->map_info_list; - map_info_t *next = NULL; - map_info_t *prev = NULL; - while (map != NULL) { - if (addr >= map->start && addr < map->end) { - next = map->next; - break; - } else if (addr >= map->end) { - /* map would be between "prev" and this entry */ - next = map; - map = NULL; - break; - } - - prev = map; - map = map->next; - } - - /* - * Show "next" then "match" then "prev" so that the addresses appear in - * ascending order (like /proc/pid/maps). - */ - dump_map(log, next, "map below", scopeFlags); - dump_map(log, map, "map for address", scopeFlags); - dump_map(log, prev, "map above", scopeFlags); -} - -static void dump_thread(const ptrace_context_t* context, log_t* log, pid_t tid, bool at_fault, - int* total_sleep_time_usec) { - wait_for_stop(tid, total_sleep_time_usec); - - dump_registers(context, log, tid, at_fault); - dump_backtrace_and_stack(context, log, tid, at_fault); - if (at_fault) { - dump_memory_and_code(context, log, tid, at_fault); - dump_nearby_maps(context, log, tid, at_fault); - } -} - -/* Return true if some thread is not detached cleanly */ -static bool dump_sibling_thread_report(const ptrace_context_t* context, - log_t* log, pid_t pid, pid_t tid, int* total_sleep_time_usec) { - char task_path[64]; - snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid); - - DIR* d = opendir(task_path); - /* Bail early if cannot open the task directory */ - if (d == NULL) { - XLOG("Cannot open /proc/%d/task\n", pid); - return false; - } - - bool detach_failed = false; - struct dirent* de; - while ((de = readdir(d)) != NULL) { - /* Ignore "." and ".." */ - if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) { - continue; - } - - /* The main thread at fault has been handled individually */ - char* end; - pid_t new_tid = strtoul(de->d_name, &end, 10); - if (*end || new_tid == tid) { - continue; - } - - /* Skip this thread if cannot ptrace it */ - if (ptrace(PTRACE_ATTACH, new_tid, 0, 0) < 0) { - continue; - } - - _LOG(log, 0, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n"); - dump_thread_info(log, pid, new_tid, false); - dump_thread(context, log, new_tid, false, total_sleep_time_usec); - - if (ptrace(PTRACE_DETACH, new_tid, 0, 0) != 0) { - LOG("ptrace detach from %d failed: %s\n", new_tid, strerror(errno)); - detach_failed = true; - } - } - - closedir(d); - return detach_failed; -} - -/* - * Reads the contents of the specified log device, filters out the entries - * that don't match the specified pid, and writes them to the tombstone file. - * - * If "tailOnly" is set, we only print the last few lines. - */ -static void dump_log_file(log_t* log, pid_t pid, const char* filename, - bool tailOnly) -{ - bool first = true; - - /* circular buffer, for "tailOnly" mode */ - const int kShortLogMaxLines = 5; - const int kShortLogLineLen = 256; - char shortLog[kShortLogMaxLines][kShortLogLineLen]; - int shortLogCount = 0; - int shortLogNext = 0; - - int logfd = open(filename, O_RDONLY | O_NONBLOCK); - if (logfd < 0) { - XLOG("Unable to open %s: %s\n", filename, strerror(errno)); - return; - } - - union { - unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1]; - struct logger_entry entry; - } log_entry; - - while (true) { - ssize_t actual = read(logfd, log_entry.buf, LOGGER_ENTRY_MAX_LEN); - if (actual < 0) { - if (errno == EINTR) { - /* interrupted by signal, retry */ - continue; - } else if (errno == EAGAIN) { - /* non-blocking EOF; we're done */ - break; - } else { - _LOG(log, 0, "Error while reading log: %s\n", - strerror(errno)); - break; - } - } else if (actual == 0) { - _LOG(log, 0, "Got zero bytes while reading log: %s\n", - strerror(errno)); - break; - } - - /* - * NOTE: if you XLOG something here, this will spin forever, - * because you will be writing as fast as you're reading. Any - * high-frequency debug diagnostics should just be written to - * the tombstone file. - */ - - struct logger_entry* entry = &log_entry.entry; - - if (entry->pid != (int32_t) pid) { - /* wrong pid, ignore */ - continue; - } - - if (first) { - _LOG(log, 0, "--------- %slog %s\n", - tailOnly ? "tail end of " : "", filename); - first = false; - } - - /* - * Msg format is: <priority:1><tag:N>\0<message:N>\0 - * - * We want to display it in the same format as "logcat -v threadtime" - * (although in this case the pid is redundant). - * - * TODO: scan for line breaks ('\n') and display each text line - * on a separate line, prefixed with the header, like logcat does. - */ - static const char* kPrioChars = "!.VDIWEFS"; - unsigned char prio = entry->msg[0]; - char* tag = entry->msg + 1; - char* msg = tag + strlen(tag) + 1; - - /* consume any trailing newlines */ - char* eatnl = msg + strlen(msg) - 1; - while (eatnl >= msg && *eatnl == '\n') { - *eatnl-- = '\0'; - } - - char prioChar = (prio < strlen(kPrioChars) ? kPrioChars[prio] : '?'); - - char timeBuf[32]; - time_t sec = (time_t) entry->sec; - struct tm tmBuf; - struct tm* ptm; - ptm = localtime_r(&sec, &tmBuf); - strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm); - - if (tailOnly) { - snprintf(shortLog[shortLogNext], kShortLogLineLen, - "%s.%03d %5d %5d %c %-8s: %s", - timeBuf, entry->nsec / 1000000, entry->pid, entry->tid, - prioChar, tag, msg); - shortLogNext = (shortLogNext + 1) % kShortLogMaxLines; - shortLogCount++; - } else { - _LOG(log, 0, "%s.%03d %5d %5d %c %-8s: %s\n", - timeBuf, entry->nsec / 1000000, entry->pid, entry->tid, - prioChar, tag, msg); - } - } - - if (tailOnly) { - int i; - - /* - * If we filled the buffer, we want to start at "next", which has - * the oldest entry. If we didn't, we want to start at zero. - */ - if (shortLogCount < kShortLogMaxLines) { - shortLogNext = 0; - } else { - shortLogCount = kShortLogMaxLines; /* cap at window size */ - } - - for (i = 0; i < shortLogCount; i++) { - _LOG(log, 0, "%s\n", shortLog[shortLogNext]); - shortLogNext = (shortLogNext + 1) % kShortLogMaxLines; - } - } - - close(logfd); -} - -/* - * Dumps the logs generated by the specified pid to the tombstone, from both - * "system" and "main" log devices. Ideally we'd interleave the output. - */ -static void dump_logs(log_t* log, pid_t pid, bool tailOnly) -{ - dump_log_file(log, pid, "/dev/log/system", tailOnly); - dump_log_file(log, pid, "/dev/log/main", tailOnly); -} - -static void dump_abort_message(log_t* log, pid_t tid, uintptr_t address) { - if (address == 0) { - return; - } - - address += sizeof(size_t); // Skip the buffer length. - - char msg[512]; - memset(msg, 0, sizeof(msg)); - char* p = &msg[0]; - while (p < &msg[sizeof(msg)]) { - uint32_t data; - if (!try_get_word_ptrace(tid, address, &data)) { - break; - } - address += sizeof(uint32_t); - - if ((*p++ = (data >> 0) & 0xff) == 0) { - break; - } - if ((*p++ = (data >> 8) & 0xff) == 0) { - break; - } - if ((*p++ = (data >> 16) & 0xff) == 0) { - break; - } - if ((*p++ = (data >> 24) & 0xff) == 0) { - break; - } - } - msg[sizeof(msg) - 1] = '\0'; - - _LOG(log, SCOPE_AT_FAULT, "Abort message: '%s'\n", msg); -} - -/* - * Dumps all information about the specified pid to the tombstone. - */ -static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, uintptr_t abort_msg_address, - bool dump_sibling_threads, int* total_sleep_time_usec) -{ - /* don't copy log messages to tombstone unless this is a dev device */ - char value[PROPERTY_VALUE_MAX]; - property_get("ro.debuggable", value, "0"); - bool want_logs = (value[0] == '1'); - - if (log->amfd >= 0) { - /* - * Activity Manager protocol: binary 32-bit network-byte-order ints for the - * pid and signal number, followed by the raw text of the dump, culminating - * in a zero byte that marks end-of-data. - */ - uint32_t datum = htonl(pid); - TEMP_FAILURE_RETRY( write(log->amfd, &datum, 4) ); - datum = htonl(signal); - TEMP_FAILURE_RETRY( write(log->amfd, &datum, 4) ); - } - - _LOG(log, SCOPE_AT_FAULT, - "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n"); - dump_build_info(log); - dump_revision_info(log); - dump_thread_info(log, pid, tid, true); - if (signal) { - dump_fault_addr(log, tid, signal); - } - dump_abort_message(log, tid, abort_msg_address); - - ptrace_context_t* context = load_ptrace_context(tid); - dump_thread(context, log, tid, true, total_sleep_time_usec); - - if (want_logs) { - dump_logs(log, pid, true); - } - - bool detach_failed = false; - if (dump_sibling_threads) { - detach_failed = dump_sibling_thread_report(context, log, pid, tid, total_sleep_time_usec); - } - - free_ptrace_context(context); - - if (want_logs) { - dump_logs(log, pid, false); - } - - /* send EOD to the Activity Manager, then wait for its ack to avoid racing ahead - * and killing the target out from under it */ - if (log->amfd >= 0) { - uint8_t eodMarker = 0; - TEMP_FAILURE_RETRY( write(log->amfd, &eodMarker, 1) ); - /* 3 sec timeout reading the ack; we're fine if that happens */ - TEMP_FAILURE_RETRY( read(log->amfd, &eodMarker, 1) ); - } - - return detach_failed; -} - -/* - * find_and_open_tombstone - find an available tombstone slot, if any, of the - * form tombstone_XX where XX is 00 to MAX_TOMBSTONES-1, inclusive. If no - * file is available, we reuse the least-recently-modified file. - * - * Returns the path of the tombstone file, allocated using malloc(). Caller must free() it. - */ -static char* find_and_open_tombstone(int* fd) -{ - unsigned long mtime = ULONG_MAX; - struct stat sb; - - /* - * XXX: Our stat.st_mtime isn't time_t. If it changes, as it probably ought - * to, our logic breaks. This check will generate a warning if that happens. - */ - typecheck(mtime, sb.st_mtime); - - /* - * In a single wolf-like pass, find an available slot and, in case none - * exist, find and record the least-recently-modified file. - */ - char path[128]; - int oldest = 0; - for (int i = 0; i < MAX_TOMBSTONES; i++) { - snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", i); - - if (!stat(path, &sb)) { - if (sb.st_mtime < mtime) { - oldest = i; - mtime = sb.st_mtime; - } - continue; - } - if (errno != ENOENT) - continue; - - *fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0600); - if (*fd < 0) - continue; /* raced ? */ - - fchown(*fd, AID_SYSTEM, AID_SYSTEM); - return strdup(path); - } - - /* we didn't find an available file, so we clobber the oldest one */ - snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", oldest); - *fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600); - if (*fd < 0) { - LOG("failed to open tombstone file '%s': %s\n", path, strerror(errno)); - return NULL; - } - fchown(*fd, AID_SYSTEM, AID_SYSTEM); - return strdup(path); -} - -static int activity_manager_connect() { - int amfd = socket(PF_UNIX, SOCK_STREAM, 0); - if (amfd >= 0) { - struct sockaddr_un address; - int err; - - memset(&address, 0, sizeof(address)); - address.sun_family = AF_UNIX; - strncpy(address.sun_path, NCRASH_SOCKET_PATH, sizeof(address.sun_path)); - err = TEMP_FAILURE_RETRY( connect(amfd, (struct sockaddr*) &address, sizeof(address)) ); - if (!err) { - struct timeval tv; - memset(&tv, 0, sizeof(tv)); - tv.tv_sec = 1; // tight leash - err = setsockopt(amfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); - if (!err) { - tv.tv_sec = 3; // 3 seconds on handshake read - err = setsockopt(amfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); - } - } - if (err) { - close(amfd); - amfd = -1; - } - } - - return amfd; -} - -char* engrave_tombstone(pid_t pid, pid_t tid, int signal, uintptr_t abort_msg_address, - bool dump_sibling_threads, bool quiet, bool* detach_failed, - int* total_sleep_time_usec) { - mkdir(TOMBSTONE_DIR, 0755); - chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM); - - if (selinux_android_restorecon(TOMBSTONE_DIR) == -1) { - *detach_failed = false; - return NULL; - } - - int fd; - char* path = find_and_open_tombstone(&fd); - if (!path) { - *detach_failed = false; - return NULL; - } - - log_t log; - log.tfd = fd; - log.amfd = activity_manager_connect(); - log.quiet = quiet; - *detach_failed = dump_crash(&log, pid, tid, signal, abort_msg_address, dump_sibling_threads, - total_sleep_time_usec); - - close(log.amfd); - close(fd); - return path; -} diff --git a/debuggerd/tombstone.cpp b/debuggerd/tombstone.cpp new file mode 100644 index 0000000..c9a2376 --- /dev/null +++ b/debuggerd/tombstone.cpp @@ -0,0 +1,774 @@ +/* + * Copyright (C) 2012-2014 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 <stddef.h> +#include <stdlib.h> +#include <signal.h> +#include <string.h> +#include <stdio.h> +#include <fcntl.h> +#include <errno.h> +#include <dirent.h> +#include <time.h> +#include <sys/ptrace.h> +#include <sys/stat.h> +#include <inttypes.h> + +#include <private/android_filesystem_config.h> + +#include <log/log.h> +#include <log/logger.h> +#include <cutils/properties.h> + +#include <backtrace/Backtrace.h> +#include <backtrace/BacktraceMap.h> + +#include <sys/socket.h> +#include <linux/un.h> + +#include <selinux/android.h> + +#include <UniquePtr.h> + +#include "machine.h" +#include "tombstone.h" +#include "backtrace.h" + +#define STACK_WORDS 16 + +#define MAX_TOMBSTONES 10 +#define TOMBSTONE_DIR "/data/tombstones" + +// Must match the path defined in NativeCrashListener.java +#define NCRASH_SOCKET_PATH "/data/system/ndebugsocket" + +#define typecheck(x,y) { \ + typeof(x) __dummy1; \ + typeof(y) __dummy2; \ + (void)(&__dummy1 == &__dummy2); } + + +static bool signal_has_address(int sig) { + switch (sig) { + case SIGILL: + case SIGFPE: + case SIGSEGV: + case SIGBUS: + return true; + default: + return false; + } +} + +static const char* get_signame(int sig) { + switch(sig) { + case SIGILL: return "SIGILL"; + case SIGABRT: return "SIGABRT"; + case SIGBUS: return "SIGBUS"; + case SIGFPE: return "SIGFPE"; + case SIGSEGV: return "SIGSEGV"; + case SIGPIPE: return "SIGPIPE"; +#ifdef SIGSTKFLT + case SIGSTKFLT: return "SIGSTKFLT"; +#endif + case SIGSTOP: return "SIGSTOP"; + default: return "?"; + } +} + +static const char* get_sigcode(int signo, int code) { + // Try the signal-specific codes... + switch (signo) { + case SIGILL: + switch (code) { + case ILL_ILLOPC: return "ILL_ILLOPC"; + case ILL_ILLOPN: return "ILL_ILLOPN"; + case ILL_ILLADR: return "ILL_ILLADR"; + case ILL_ILLTRP: return "ILL_ILLTRP"; + case ILL_PRVOPC: return "ILL_PRVOPC"; + case ILL_PRVREG: return "ILL_PRVREG"; + case ILL_COPROC: return "ILL_COPROC"; + case ILL_BADSTK: return "ILL_BADSTK"; + } + break; + case SIGBUS: + switch (code) { + case BUS_ADRALN: return "BUS_ADRALN"; + case BUS_ADRERR: return "BUS_ADRERR"; + case BUS_OBJERR: return "BUS_OBJERR"; + } + break; + case SIGFPE: + switch (code) { + case FPE_INTDIV: return "FPE_INTDIV"; + case FPE_INTOVF: return "FPE_INTOVF"; + case FPE_FLTDIV: return "FPE_FLTDIV"; + case FPE_FLTOVF: return "FPE_FLTOVF"; + case FPE_FLTUND: return "FPE_FLTUND"; + case FPE_FLTRES: return "FPE_FLTRES"; + case FPE_FLTINV: return "FPE_FLTINV"; + case FPE_FLTSUB: return "FPE_FLTSUB"; + } + break; + case SIGSEGV: + switch (code) { + case SEGV_MAPERR: return "SEGV_MAPERR"; + 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, SCOPE_AT_FAULT, "Revision: '%s'\n", revision); +} + +static void dump_build_info(log_t* log) { + char fingerprint[PROPERTY_VALUE_MAX]; + + property_get("ro.build.fingerprint", fingerprint, "unknown"); + + _LOG(log, SCOPE_AT_FAULT, "Build fingerprint: '%s'\n", fingerprint); +} + +static void dump_fault_addr(log_t* log, pid_t tid, int sig) { + siginfo_t si; + + memset(&si, 0, sizeof(si)); + if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si)){ + _LOG(log, SCOPE_AT_FAULT, "cannot get siginfo: %s\n", strerror(errno)); + } else if (signal_has_address(sig)) { + _LOG(log, SCOPE_AT_FAULT, "signal %d (%s), code %d (%s), fault addr %0*" PRIxPTR "\n", + sig, get_signame(sig), si.si_code, get_sigcode(sig, si.si_code), + sizeof(uintptr_t)*2, reinterpret_cast<uintptr_t>(si.si_addr)); + } else { + _LOG(log, SCOPE_AT_FAULT, "signal %d (%s), code %d (%s), fault addr --------\n", + sig, get_signame(sig), si.si_code, get_sigcode(sig, si.si_code)); + } +} + +static void dump_thread_info(log_t* log, pid_t pid, pid_t tid, int scope_flags) { + char path[64]; + char threadnamebuf[1024]; + char* threadname = NULL; + FILE *fp; + + snprintf(path, sizeof(path), "/proc/%d/comm", tid); + if ((fp = fopen(path, "r"))) { + threadname = fgets(threadnamebuf, sizeof(threadnamebuf), fp); + fclose(fp); + if (threadname) { + size_t len = strlen(threadname); + if (len && threadname[len - 1] == '\n') { + threadname[len - 1] = '\0'; + } + } + } + + if (IS_AT_FAULT(scope_flags)) { + char procnamebuf[1024]; + char* procname = NULL; + + snprintf(path, sizeof(path), "/proc/%d/cmdline", pid); + if ((fp = fopen(path, "r"))) { + procname = fgets(procnamebuf, sizeof(procnamebuf), fp); + fclose(fp); + } + + _LOG(log, SCOPE_AT_FAULT, "pid: %d, tid: %d, name: %s >>> %s <<<\n", pid, tid, + threadname ? threadname : "UNKNOWN", procname ? procname : "UNKNOWN"); + } else { + _LOG(log, 0, "pid: %d, tid: %d, name: %s\n", pid, tid, threadname ? threadname : "UNKNOWN"); + } +} + +static void dump_stack_segment( + Backtrace* backtrace, log_t* log, int scope_flags, uintptr_t* sp, size_t words, int label) { + for (size_t i = 0; i < words; i++) { + uint32_t stack_content; + if (!backtrace->ReadWord(*sp, &stack_content)) { + break; + } + + const backtrace_map_t* map = backtrace->FindMap(stack_content); + const char* map_name; + if (!map) { + map_name = ""; + } else { + map_name = map->name.c_str(); + } + uintptr_t offset = 0; + std::string func_name(backtrace->GetFunctionName(stack_content, &offset)); + if (!func_name.empty()) { + if (!i && label >= 0) { + if (offset) { + _LOG(log, scope_flags, " #%02d %08x %08x %s (%s+%u)\n", + label, *sp, stack_content, map_name, func_name.c_str(), offset); + } else { + _LOG(log, scope_flags, " #%02d %08x %08x %s (%s)\n", + label, *sp, stack_content, map_name, func_name.c_str()); + } + } else { + if (offset) { + _LOG(log, scope_flags, " %08x %08x %s (%s+%u)\n", + *sp, stack_content, map_name, func_name.c_str(), offset); + } else { + _LOG(log, scope_flags, " %08x %08x %s (%s)\n", + *sp, stack_content, map_name, func_name.c_str()); + } + } + } else { + if (!i && label >= 0) { + _LOG(log, scope_flags, " #%02d %08x %08x %s\n", + label, *sp, stack_content, map_name); + } else { + _LOG(log, scope_flags, " %08x %08x %s\n", + *sp, stack_content, map_name); + } + } + + *sp += sizeof(uint32_t); + } +} + +static void dump_stack(Backtrace* backtrace, log_t* log, int scope_flags) { + size_t first = 0, last; + for (size_t i = 0; i < backtrace->NumFrames(); i++) { + const backtrace_frame_data_t* frame = backtrace->GetFrame(i); + if (frame->sp) { + if (!first) { + first = i+1; + } + last = i; + } + } + if (!first) { + return; + } + first--; + + scope_flags |= SCOPE_SENSITIVE; + + // Dump a few words before the first frame. + uintptr_t sp = backtrace->GetFrame(first)->sp - STACK_WORDS * sizeof(uint32_t); + dump_stack_segment(backtrace, log, scope_flags, &sp, STACK_WORDS, -1); + + // Dump a few words from all successive frames. + // Only log the first 3 frames, put the rest in the tombstone. + for (size_t i = first; i <= last; i++) { + const backtrace_frame_data_t* frame = backtrace->GetFrame(i); + if (sp != frame->sp) { + _LOG(log, scope_flags, " ........ ........\n"); + sp = frame->sp; + } + if (i - first == 3) { + scope_flags &= (~SCOPE_AT_FAULT); + } + if (i == last) { + dump_stack_segment(backtrace, log, scope_flags, &sp, STACK_WORDS, i); + if (sp < frame->sp + frame->stack_size) { + _LOG(log, scope_flags, " ........ ........\n"); + } + } else { + size_t words = frame->stack_size / sizeof(uint32_t); + if (words == 0) { + words = 1; + } else if (words > STACK_WORDS) { + words = STACK_WORDS; + } + dump_stack_segment(backtrace, log, scope_flags, &sp, words, i); + } + } +} + +static void dump_backtrace_and_stack(Backtrace* backtrace, log_t* log, int scope_flags) { + if (backtrace->NumFrames()) { + _LOG(log, scope_flags, "\nbacktrace:\n"); + dump_backtrace_to_log(backtrace, log, scope_flags, " "); + + _LOG(log, scope_flags, "\nstack:\n"); + dump_stack(backtrace, log, scope_flags); + } +} + +static void dump_map(log_t* log, const backtrace_map_t* map, const char* what, int scope_flags) { + if (map != NULL) { + _LOG(log, scope_flags, " %08x-%08x %c%c%c %s\n", map->start, map->end, + (map->flags & PROT_READ) ? 'r' : '-', (map->flags & PROT_WRITE) ? 'w' : '-', + (map->flags & PROT_EXEC) ? 'x' : '-', map->name.c_str()); + } else { + _LOG(log, scope_flags, " (no %s)\n", what); + } +} + +static void dump_nearby_maps(BacktraceMap* map, log_t* log, pid_t tid, int scope_flags) { + scope_flags |= SCOPE_SENSITIVE; + siginfo_t si; + memset(&si, 0, sizeof(si)); + if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si)) { + _LOG(log, scope_flags, "cannot get siginfo for %d: %s\n", tid, strerror(errno)); + return; + } + if (!signal_has_address(si.si_signo)) { + return; + } + + uintptr_t addr = reinterpret_cast<uintptr_t>(si.si_addr); + addr &= ~0xfff; // round to 4K page boundary + if (addr == 0) { // null-pointer deref + return; + } + + _LOG(log, scope_flags, "\nmemory map around fault addr %" PRIxPTR ":\n", + reinterpret_cast<uintptr_t>(si.si_addr)); + + // Search for a match, or for a hole where the match would be. The list + // is backward from the file content, so it starts at high addresses. + const backtrace_map_t* cur_map = NULL; + const backtrace_map_t* next_map = NULL; + const backtrace_map_t* prev_map = NULL; + for (BacktraceMap::const_iterator it = map->begin(); it != map->end(); ++it) { + if (addr >= it->start && addr < it->end) { + cur_map = &*it; + if (it != map->begin()) { + prev_map = &*(it-1); + } + if (++it != map->end()) { + next_map = &*it; + } + break; + } + } + + // Show the map address in ascending order (like /proc/pid/maps). + dump_map(log, prev_map, "map below", scope_flags); + dump_map(log, cur_map, "map for address", scope_flags); + dump_map(log, next_map, "map above", scope_flags); +} + +static void dump_thread( + Backtrace* backtrace, log_t* log, int scope_flags, int* total_sleep_time_usec) { + wait_for_stop(backtrace->Tid(), total_sleep_time_usec); + + dump_registers(log, backtrace->Tid(), scope_flags); + dump_backtrace_and_stack(backtrace, log, scope_flags); + if (IS_AT_FAULT(scope_flags)) { + dump_memory_and_code(log, backtrace->Tid(), scope_flags); + dump_nearby_maps(backtrace->GetMap(), log, backtrace->Tid(), scope_flags); + } +} + +// Return true if some thread is not detached cleanly +static bool dump_sibling_thread_report( + log_t* log, pid_t pid, pid_t tid, int* total_sleep_time_usec, BacktraceMap* map) { + char task_path[64]; + snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid); + + DIR* d = opendir(task_path); + // Bail early if the task directory cannot be opened + if (d == NULL) { + XLOG("Cannot open /proc/%d/task\n", pid); + return false; + } + + bool detach_failed = false; + struct dirent* de; + while ((de = readdir(d)) != NULL) { + // Ignore "." and ".." + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) { + continue; + } + + // The main thread at fault has been handled individually + char* end; + pid_t new_tid = strtoul(de->d_name, &end, 10); + if (*end || new_tid == tid) { + continue; + } + + // Skip this thread if cannot ptrace it + if (ptrace(PTRACE_ATTACH, new_tid, 0, 0) < 0) { + continue; + } + + _LOG(log, 0, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n"); + dump_thread_info(log, pid, new_tid, 0); + + UniquePtr<Backtrace> backtrace(Backtrace::Create(pid, new_tid, map)); + if (backtrace->Unwind(0)) { + dump_thread(backtrace.get(), log, 0, total_sleep_time_usec); + } + + if (ptrace(PTRACE_DETACH, new_tid, 0, 0) != 0) { + LOG("ptrace detach from %d failed: %s\n", new_tid, strerror(errno)); + detach_failed = true; + } + } + + closedir(d); + return detach_failed; +} + +// Reads the contents of the specified log device, filters out the entries +// that don't match the specified pid, and writes them to the tombstone file. +// +// If "tail" is set, we only print the last few lines. +static void dump_log_file(log_t* log, pid_t pid, const char* filename, + unsigned int tail) { + bool first = true; + struct logger_list *logger_list; + + logger_list = android_logger_list_open( + android_name_to_log_id(filename), O_RDONLY | O_NONBLOCK, tail, pid); + + if (!logger_list) { + XLOG("Unable to open %s: %s\n", filename, strerror(errno)); + return; + } + + struct log_msg log_entry; + + while (true) { + ssize_t actual = android_logger_list_read(logger_list, &log_entry); + + if (actual < 0) { + if (actual == -EINTR) { + // interrupted by signal, retry + continue; + } else if (actual == -EAGAIN) { + // non-blocking EOF; we're done + break; + } else { + _LOG(log, 0, "Error while reading log: %s\n", + strerror(-actual)); + break; + } + } else if (actual == 0) { + _LOG(log, 0, "Got zero bytes while reading log: %s\n", + strerror(errno)); + break; + } + + // NOTE: if you XLOG something here, this will spin forever, + // because you will be writing as fast as you're reading. Any + // high-frequency debug diagnostics should just be written to + // the tombstone file. + struct logger_entry* entry = &log_entry.entry_v1; + + if (entry->pid != static_cast<int32_t>(pid)) { + // wrong pid, ignore + continue; + } + + if (first) { + _LOG(log, 0, "--------- %slog %s\n", + tail ? "tail end of " : "", filename); + first = false; + } + + // Msg format is: <priority:1><tag:N>\0<message:N>\0 + // + // We want to display it in the same format as "logcat -v threadtime" + // (although in this case the pid is redundant). + static const char* kPrioChars = "!.VDIWEFS"; + unsigned hdr_size = log_entry.entry.hdr_size; + if (!hdr_size) { + hdr_size = sizeof(log_entry.entry_v1); + } + char* msg = (char *)log_entry.buf + hdr_size; + unsigned char prio = msg[0]; + char* tag = msg + 1; + msg = tag + strlen(tag) + 1; + + // consume any trailing newlines + char* nl = msg + strlen(msg) - 1; + while (nl >= msg && *nl == '\n') { + *nl-- = '\0'; + } + + char prioChar = (prio < strlen(kPrioChars) ? kPrioChars[prio] : '?'); + + char timeBuf[32]; + time_t sec = static_cast<time_t>(entry->sec); + struct tm tmBuf; + struct tm* ptm; + ptm = localtime_r(&sec, &tmBuf); + strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm); + + // Look for line breaks ('\n') and display each text line + // on a separate line, prefixed with the header, like logcat does. + do { + nl = strchr(msg, '\n'); + if (nl) { + *nl = '\0'; + ++nl; + } + + _LOG(log, 0, "%s.%03d %5d %5d %c %-8s: %s\n", + timeBuf, entry->nsec / 1000000, entry->pid, entry->tid, + prioChar, tag, msg); + + } while ((msg = nl)); + } + + android_logger_list_free(logger_list); +} + +// Dumps the logs generated by the specified pid to the tombstone, from both +// "system" and "main" log devices. Ideally we'd interleave the output. +static void dump_logs(log_t* log, pid_t pid, unsigned tail) { + dump_log_file(log, pid, "system", tail); + dump_log_file(log, pid, "main", tail); +} + +static void dump_abort_message(Backtrace* backtrace, log_t* log, uintptr_t address) { + if (address == 0) { + return; + } + + address += sizeof(size_t); // Skip the buffer length. + + char msg[512]; + memset(msg, 0, sizeof(msg)); + char* p = &msg[0]; + while (p < &msg[sizeof(msg)]) { + uint32_t data; + if (!backtrace->ReadWord(address, &data)) { + break; + } + address += sizeof(uint32_t); + + if ((*p++ = (data >> 0) & 0xff) == 0) { + break; + } + if ((*p++ = (data >> 8) & 0xff) == 0) { + break; + } + if ((*p++ = (data >> 16) & 0xff) == 0) { + break; + } + if ((*p++ = (data >> 24) & 0xff) == 0) { + break; + } + } + msg[sizeof(msg) - 1] = '\0'; + + _LOG(log, SCOPE_AT_FAULT, "Abort message: '%s'\n", msg); +} + +// Dumps all information about the specified pid to the tombstone. +static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, uintptr_t abort_msg_address, + bool dump_sibling_threads, int* total_sleep_time_usec) { + // don't copy log messages to tombstone unless this is a dev device + char value[PROPERTY_VALUE_MAX]; + property_get("ro.debuggable", value, "0"); + bool want_logs = (value[0] == '1'); + + if (log->amfd >= 0) { + // Activity Manager protocol: binary 32-bit network-byte-order ints for the + // pid and signal number, followed by the raw text of the dump, culminating + // in a zero byte that marks end-of-data. + uint32_t datum = htonl(pid); + TEMP_FAILURE_RETRY( write(log->amfd, &datum, 4) ); + datum = htonl(signal); + TEMP_FAILURE_RETRY( write(log->amfd, &datum, 4) ); + } + + _LOG(log, SCOPE_AT_FAULT, + "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n"); + dump_build_info(log); + dump_revision_info(log); + dump_thread_info(log, pid, tid, SCOPE_AT_FAULT); + if (signal) { + dump_fault_addr(log, tid, signal); + } + + UniquePtr<BacktraceMap> map(BacktraceMap::Create(pid)); + UniquePtr<Backtrace> backtrace(Backtrace::Create(pid, tid, map.get())); + if (backtrace->Unwind(0)) { + dump_abort_message(backtrace.get(), log, abort_msg_address); + dump_thread(backtrace.get(), log, SCOPE_AT_FAULT, total_sleep_time_usec); + } + + if (want_logs) { + dump_logs(log, pid, 5); + } + + bool detach_failed = false; + if (dump_sibling_threads) { + detach_failed = dump_sibling_thread_report(log, pid, tid, total_sleep_time_usec, map.get()); + } + + if (want_logs) { + dump_logs(log, pid, 0); + } + + // send EOD to the Activity Manager, then wait for its ack to avoid racing ahead + // and killing the target out from under it + if (log->amfd >= 0) { + uint8_t eodMarker = 0; + TEMP_FAILURE_RETRY( write(log->amfd, &eodMarker, 1) ); + // 3 sec timeout reading the ack; we're fine if that happens + TEMP_FAILURE_RETRY( read(log->amfd, &eodMarker, 1) ); + } + + return detach_failed; +} + +// find_and_open_tombstone - find an available tombstone slot, if any, of the +// form tombstone_XX where XX is 00 to MAX_TOMBSTONES-1, inclusive. If no +// file is available, we reuse the least-recently-modified file. +// +// Returns the path of the tombstone file, allocated using malloc(). Caller must free() it. +static char* find_and_open_tombstone(int* fd) { + unsigned long mtime = ULONG_MAX; + struct stat sb; + + // XXX: Our stat.st_mtime isn't time_t. If it changes, as it probably ought + // to, our logic breaks. This check will generate a warning if that happens. + typecheck(mtime, sb.st_mtime); + + // In a single wolf-like pass, find an available slot and, in case none + // exist, find and record the least-recently-modified file. + char path[128]; + int oldest = 0; + for (int i = 0; i < MAX_TOMBSTONES; i++) { + snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", i); + + if (!stat(path, &sb)) { + if (sb.st_mtime < mtime) { + oldest = i; + mtime = sb.st_mtime; + } + continue; + } + if (errno != ENOENT) + continue; + + *fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0600); + if (*fd < 0) + continue; // raced ? + + fchown(*fd, AID_SYSTEM, AID_SYSTEM); + return strdup(path); + } + + // we didn't find an available file, so we clobber the oldest one + snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", oldest); + *fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600); + if (*fd < 0) { + LOG("failed to open tombstone file '%s': %s\n", path, strerror(errno)); + return NULL; + } + fchown(*fd, AID_SYSTEM, AID_SYSTEM); + return strdup(path); +} + +static int activity_manager_connect() { + int amfd = socket(PF_UNIX, SOCK_STREAM, 0); + if (amfd >= 0) { + struct sockaddr_un address; + int err; + + memset(&address, 0, sizeof(address)); + address.sun_family = AF_UNIX; + strncpy(address.sun_path, NCRASH_SOCKET_PATH, sizeof(address.sun_path)); + err = TEMP_FAILURE_RETRY(connect( + amfd, reinterpret_cast<struct sockaddr*>(&address), sizeof(address))); + if (!err) { + struct timeval tv; + memset(&tv, 0, sizeof(tv)); + tv.tv_sec = 1; // tight leash + err = setsockopt(amfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); + if (!err) { + tv.tv_sec = 3; // 3 seconds on handshake read + err = setsockopt(amfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + } + } + if (err) { + close(amfd); + amfd = -1; + } + } + + return amfd; +} + +char* engrave_tombstone( + pid_t pid, pid_t tid, int signal, uintptr_t abort_msg_address, bool dump_sibling_threads, + bool quiet, bool* detach_failed, int* total_sleep_time_usec) { + mkdir(TOMBSTONE_DIR, 0755); + chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM); + + int fd = -1; + char* path = NULL; + if (selinux_android_restorecon(TOMBSTONE_DIR) == 0) { + path = find_and_open_tombstone(&fd); + } else { + LOG("Failed to restore security context, not writing tombstone.\n"); + } + + if (fd < 0 && quiet) { + LOG("Skipping tombstone write, nothing to do.\n"); + *detach_failed = false; + return NULL; + } + + log_t log; + log.tfd = fd; + // Preserve amfd since it can be modified through the calls below without + // being closed. + int amfd = activity_manager_connect(); + log.amfd = amfd; + log.quiet = quiet; + *detach_failed = dump_crash( + &log, pid, tid, signal, abort_msg_address, dump_sibling_threads, total_sleep_time_usec); + + // Either of these file descriptors can be -1, any error is ignored. + close(amfd); + close(fd); + + return path; +} diff --git a/debuggerd/tombstone.h b/debuggerd/tombstone.h index d4a1a96..e9878bf 100644 --- a/debuggerd/tombstone.h +++ b/debuggerd/tombstone.h @@ -21,8 +21,6 @@ #include <stdbool.h> #include <sys/types.h> -#include <corkscrew/ptrace.h> - /* Creates a tombstone file and writes the crash dump to it. * Returns the path of the tombstone, which must be freed using free(). */ char* engrave_tombstone(pid_t pid, pid_t tid, int signal, uintptr_t abort_msg_address, diff --git a/debuggerd/utility.c b/debuggerd/utility.c deleted file mode 100644 index 41be982..0000000 --- a/debuggerd/utility.c +++ /dev/null @@ -1,130 +0,0 @@ -/* system/debuggerd/utility.c -** -** Copyright 2008, 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 <stddef.h> -#include <stdbool.h> -#include <stdio.h> -#include <string.h> -#include <errno.h> -#include <unistd.h> -#include <signal.h> -#include <log/logd.h> -#include <sys/ptrace.h> -#include <sys/wait.h> -#include <arpa/inet.h> -#include <assert.h> - -#include "utility.h" - -const int sleep_time_usec = 50000; /* 0.05 seconds */ -const int max_total_sleep_usec = 10000000; /* 10 seconds */ - -static int write_to_am(int fd, const char* buf, int len) { - int to_write = len; - while (to_write > 0) { - int written = TEMP_FAILURE_RETRY( write(fd, buf + len - to_write, to_write) ); - if (written < 0) { - /* hard failure */ - LOG("AM write failure (%d / %s)\n", errno, strerror(errno)); - return -1; - } - to_write -= written; - } - return len; -} - -void _LOG(log_t* log, int scopeFlags, const char *fmt, ...) { - char buf[512]; - bool want_tfd_write; - bool want_log_write; - bool want_amfd_write; - int len = 0; - - va_list ap; - va_start(ap, fmt); - - // where is the information going to go? - want_tfd_write = log && log->tfd >= 0; - want_log_write = IS_AT_FAULT(scopeFlags) && (!log || !log->quiet); - want_amfd_write = IS_AT_FAULT(scopeFlags) && !IS_SENSITIVE(scopeFlags) && log && log->amfd >= 0; - - // if we're going to need the literal string, generate it once here - if (want_tfd_write || want_amfd_write) { - vsnprintf(buf, sizeof(buf), fmt, ap); - len = strlen(buf); - } - - if (want_tfd_write) { - write(log->tfd, buf, len); - } - - if (want_log_write) { - // whatever goes to logcat also goes to the Activity Manager - __android_log_vprint(ANDROID_LOG_INFO, "DEBUG", fmt, ap); - if (want_amfd_write && len > 0) { - int written = write_to_am(log->amfd, buf, len); - if (written <= 0) { - // timeout or other failure on write; stop informing the activity manager - log->amfd = -1; - } - } - } - va_end(ap); -} - -int wait_for_signal(pid_t tid, int* total_sleep_time_usec) { - for (;;) { - int status; - pid_t n = waitpid(tid, &status, __WALL | WNOHANG); - if (n < 0) { - if(errno == EAGAIN) continue; - LOG("waitpid failed: %s\n", strerror(errno)); - return -1; - } else if (n > 0) { - XLOG("waitpid: n=%d status=%08x\n", n, status); - if (WIFSTOPPED(status)) { - return WSTOPSIG(status); - } else { - LOG("unexpected waitpid response: n=%d, status=%08x\n", n, status); - return -1; - } - } - - if (*total_sleep_time_usec > max_total_sleep_usec) { - LOG("timed out waiting for tid=%d to die\n", tid); - return -1; - } - - /* not ready yet */ - XLOG("not ready yet\n"); - usleep(sleep_time_usec); - *total_sleep_time_usec += sleep_time_usec; - } -} - -void wait_for_stop(pid_t tid, int* total_sleep_time_usec) { - siginfo_t si; - while (TEMP_FAILURE_RETRY(ptrace(PTRACE_GETSIGINFO, tid, 0, &si)) < 0 && errno == ESRCH) { - if (*total_sleep_time_usec > max_total_sleep_usec) { - LOG("timed out waiting for tid=%d to stop\n", tid); - break; - } - - usleep(sleep_time_usec); - *total_sleep_time_usec += sleep_time_usec; - } -} diff --git a/debuggerd/utility.cpp b/debuggerd/utility.cpp new file mode 100644 index 0000000..35f061e --- /dev/null +++ b/debuggerd/utility.cpp @@ -0,0 +1,129 @@ +/* + * Copyright 2008, 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 <stddef.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <signal.h> +#include <log/logd.h> +#include <sys/ptrace.h> +#include <sys/wait.h> +#include <arpa/inet.h> +#include <assert.h> + +#include "utility.h" + +const int sleep_time_usec = 50000; // 0.05 seconds +const int max_total_sleep_usec = 10000000; // 10 seconds + +static int write_to_am(int fd, const char* buf, int len) { + int to_write = len; + while (to_write > 0) { + int written = TEMP_FAILURE_RETRY( write(fd, buf + len - to_write, to_write) ); + if (written < 0) { + // hard failure + LOG("AM write failure (%d / %s)\n", errno, strerror(errno)); + return -1; + } + to_write -= written; + } + return len; +} + +void _LOG(log_t* log, int scopeFlags, const char* fmt, ...) { + char buf[512]; + bool want_tfd_write; + bool want_log_write; + bool want_amfd_write; + int len = 0; + + va_list ap; + va_start(ap, fmt); + + // where is the information going to go? + want_tfd_write = log && log->tfd >= 0; + want_log_write = IS_AT_FAULT(scopeFlags) && (!log || !log->quiet); + want_amfd_write = IS_AT_FAULT(scopeFlags) && !IS_SENSITIVE(scopeFlags) && log && log->amfd >= 0; + + // if we're going to need the literal string, generate it once here + if (want_tfd_write || want_amfd_write) { + vsnprintf(buf, sizeof(buf), fmt, ap); + len = strlen(buf); + } + + if (want_tfd_write) { + write(log->tfd, buf, len); + } + + if (want_log_write) { + // whatever goes to logcat also goes to the Activity Manager + __android_log_vprint(ANDROID_LOG_INFO, "DEBUG", fmt, ap); + if (want_amfd_write && len > 0) { + int written = write_to_am(log->amfd, buf, len); + if (written <= 0) { + // timeout or other failure on write; stop informing the activity manager + log->amfd = -1; + } + } + } + va_end(ap); +} + +int wait_for_signal(pid_t tid, int* total_sleep_time_usec) { + for (;;) { + int status; + pid_t n = waitpid(tid, &status, __WALL | WNOHANG); + if (n < 0) { + if (errno == EAGAIN) + continue; + LOG("waitpid failed: %s\n", strerror(errno)); + return -1; + } else if (n > 0) { + XLOG("waitpid: n=%d status=%08x\n", n, status); + if (WIFSTOPPED(status)) { + return WSTOPSIG(status); + } else { + LOG("unexpected waitpid response: n=%d, status=%08x\n", n, status); + return -1; + } + } + + if (*total_sleep_time_usec > max_total_sleep_usec) { + LOG("timed out waiting for tid=%d to die\n", tid); + return -1; + } + + // not ready yet + XLOG("not ready yet\n"); + usleep(sleep_time_usec); + *total_sleep_time_usec += sleep_time_usec; + } +} + +void wait_for_stop(pid_t tid, int* total_sleep_time_usec) { + siginfo_t si; + while (TEMP_FAILURE_RETRY(ptrace(PTRACE_GETSIGINFO, tid, 0, &si)) < 0 && errno == ESRCH) { + if (*total_sleep_time_usec > max_total_sleep_usec) { + LOG("timed out waiting for tid=%d to stop\n", tid); + break; + } + + usleep(sleep_time_usec); + *total_sleep_time_usec += sleep_time_usec; + } +} diff --git a/debuggerd/x86/machine.c b/debuggerd/x86/machine.c deleted file mode 100644 index af79092..0000000 --- a/debuggerd/x86/machine.c +++ /dev/null @@ -1,58 +0,0 @@ -/* system/debuggerd/debuggerd.c -** -** Copyright 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. -*/ - -#include <stddef.h> -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> -#include <stdio.h> -#include <errno.h> -#include <sys/types.h> -#include <sys/ptrace.h> - -#include <corkscrew/ptrace.h> - -#include <linux/user.h> - -#include "../utility.h" -#include "../machine.h" - -void dump_memory_and_code(const ptrace_context_t* context __attribute((unused)), - log_t* log, pid_t tid, bool at_fault) { -} - -void dump_registers(const ptrace_context_t* context __attribute((unused)), - log_t* log, pid_t tid, bool at_fault) { - struct pt_regs_x86 r; - int scopeFlags = (at_fault ? SCOPE_AT_FAULT : 0); - - if(ptrace(PTRACE_GETREGS, tid, 0, &r)) { - _LOG(log, scopeFlags, "cannot get registers: %s\n", strerror(errno)); - return; - } - //if there is no stack, no print just like arm - if(!r.ebp) - return; - _LOG(log, scopeFlags, " eax %08x ebx %08x ecx %08x edx %08x\n", - r.eax, r.ebx, r.ecx, r.edx); - _LOG(log, scopeFlags, " esi %08x edi %08x\n", - r.esi, r.edi); - _LOG(log, scopeFlags, " xcs %08x xds %08x xes %08x xfs %08x xss %08x\n", - r.xcs, r.xds, r.xes, r.xfs, r.xss); - _LOG(log, scopeFlags, " eip %08x ebp %08x esp %08x flags %08x\n", - r.eip, r.ebp, r.esp, r.eflags); -} diff --git a/debuggerd/x86/machine.cpp b/debuggerd/x86/machine.cpp new file mode 100644 index 0000000..141f19a --- /dev/null +++ b/debuggerd/x86/machine.cpp @@ -0,0 +1,45 @@ +/* + * Copyright 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. + */ + +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/ptrace.h> + +#include "../utility.h" +#include "../machine.h" + +void dump_memory_and_code(log_t* log, pid_t tid, int scope_flags) { +} + +void dump_registers(log_t* log, pid_t tid, int scope_flags) { + struct pt_regs r; + if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) { + _LOG(log, scope_flags, "cannot get registers: %s\n", strerror(errno)); + return; + } + _LOG(log, scope_flags, " eax %08lx ebx %08lx ecx %08lx edx %08lx\n", + r.eax, r.ebx, r.ecx, r.edx); + _LOG(log, scope_flags, " esi %08lx edi %08lx\n", + r.esi, r.edi); + _LOG(log, scope_flags, " xcs %08x xds %08x xes %08x xfs %08x xss %08x\n", + r.xcs, r.xds, r.xes, r.xfs, r.xss); + _LOG(log, scope_flags, " eip %08lx ebp %08lx esp %08lx flags %08lx\n", + r.eip, r.ebp, r.esp, r.eflags); +} diff --git a/fastboot/Android.mk b/fastboot/Android.mk index 1189e1f..05ddf2a 100644 --- a/fastboot/Android.mk +++ b/fastboot/Android.mk @@ -18,9 +18,10 @@ include $(CLEAR_VARS) LOCAL_C_INCLUDES := $(LOCAL_PATH)/../mkbootimg \ $(LOCAL_PATH)/../../extras/ext4_utils -LOCAL_SRC_FILES := protocol.c engine.c bootimg.c fastboot.c +LOCAL_SRC_FILES := protocol.c engine.c bootimg.c fastboot.c util.c fs.c LOCAL_MODULE := fastboot LOCAL_MODULE_TAGS := debug +LOCAL_CFLAGS += -std=gnu99 ifeq ($(HOST_OS),linux) LOCAL_SRC_FILES += usb_linux.c util_linux.c @@ -69,7 +70,7 @@ $(call dist-for-goals,dist_files sdk,$(LOCAL_BUILT_MODULE)) ifeq ($(HOST_OS),linux) include $(CLEAR_VARS) -LOCAL_SRC_FILES := usbtest.c usb_linux.c +LOCAL_SRC_FILES := usbtest.c usb_linux.c util.c LOCAL_MODULE := usbtest include $(BUILD_HOST_EXECUTABLE) endif diff --git a/fastboot/engine.c b/fastboot/engine.c index b07e742..0fab703 100644 --- a/fastboot/engine.c +++ b/fastboot/engine.c @@ -27,7 +27,7 @@ */ #include "fastboot.h" -#include "make_ext4fs.h" +#include "fs.h" #include <errno.h> #include <stdio.h> @@ -36,7 +36,6 @@ #include <stdbool.h> #include <string.h> #include <sys/stat.h> -#include <sys/time.h> #include <sys/types.h> #include <unistd.h> @@ -48,34 +47,12 @@ #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) -double now() -{ - struct timeval tv; - gettimeofday(&tv, NULL); - return (double)tv.tv_sec + (double)tv.tv_usec / 1000000; -} - -char *mkmsg(const char *fmt, ...) -{ - char buf[256]; - char *s; - va_list ap; - - va_start(ap, fmt); - vsprintf(buf, fmt, ap); - va_end(ap); - - s = strdup(buf); - if (s == 0) die("out of memory"); - return s; -} - #define OP_DOWNLOAD 1 #define OP_COMMAND 2 #define OP_QUERY 3 #define OP_NOTICE 4 -#define OP_FORMAT 5 -#define OP_DOWNLOAD_SPARSE 6 +#define OP_DOWNLOAD_SPARSE 5 +#define OP_WAIT_FOR_DISCONNECT 6 typedef struct Action Action; @@ -101,14 +78,7 @@ static Action *action_list = 0; static Action *action_last = 0; -struct image_data { - long long partition_size; - long long image_size; // real size of image file - void *buffer; -}; -void generate_ext4_image(struct image_data *image); -void cleanup_image(struct image_data *image); int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...) { @@ -124,24 +94,6 @@ int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...) return fb_command_response(usb, cmd, response); } -struct generator { - char *fs_type; - - /* generate image and return it as image->buffer. - * size of the buffer returned as image->image_size. - * - * image->partition_size specifies what is the size of the - * file partition we generate image for. - */ - void (*generate)(struct image_data *image); - - /* it cleans the buffer allocated during image creation. - * this function probably does free() or munmap(). - */ - void (*cleanup)(struct image_data *image); -} generators[] = { - { "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 @@ -152,8 +104,7 @@ struct generator { */ int fb_format_supported(usb_handle *usb, const char *partition) { - char response[FB_RESPONSE_SZ+1]; - struct generator *generator = NULL; + char response[FB_RESPONSE_SZ + 1] = {0,}; int status; unsigned int i; @@ -162,18 +113,7 @@ int fb_format_supported(usb_handle *usb, const char *partition) 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; + return !!fs_get_generator(response); } static int cb_default(Action *a, int status, char *resp) @@ -227,163 +167,6 @@ void fb_queue_erase(const char *ptn) a->msg = mkmsg("erasing '%s'", ptn); } -/* Loads file content into buffer. Returns NULL on error. */ -static void *load_buffer(int fd, off_t size) -{ - void *buffer; - -#ifdef USE_MINGW - ssize_t count = 0; - - // mmap is more efficient but mingw does not support it. - // In this case we read whole image into memory buffer. - buffer = malloc(size); - if (!buffer) { - perror("malloc"); - return NULL; - } - - lseek(fd, 0, SEEK_SET); - while(count < size) { - ssize_t actually_read = read(fd, (char*)buffer+count, size-count); - - if (actually_read == 0) { - break; - } - if (actually_read < 0) { - if (errno == EINTR) { - continue; - } - perror("read"); - free(buffer); - return NULL; - } - - count += actually_read; - } -#else - buffer = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); - if (buffer == MAP_FAILED) { - perror("mmap"); - return NULL; - } -#endif - - return buffer; -} - -void cleanup_image(struct image_data *image) -{ -#ifdef USE_MINGW - free(image->buffer); -#else - munmap(image->buffer, image->image_size); -#endif -} - -void generate_ext4_image(struct image_data *image) -{ - int fd; - struct stat st; - - fd = fileno(tmpfile()); - make_ext4fs_sparse_fd(fd, image->partition_size, NULL, NULL); - - fstat(fd, &st); - image->image_size = st.st_size; - image->buffer = load_buffer(fd, st.st_size); - - close(fd); -} - -int fb_format(Action *a, usb_handle *usb, int skip_if_not_supported) -{ - const char *partition = a->cmd; - char response[FB_RESPONSE_SZ+1]; - int status = 0; - struct image_data image; - struct generator *generator = NULL; - int fd; - unsigned i; - char cmd[CMD_SIZE]; - - status = fb_getvar(usb, response, "partition-type:%s", partition); - if (status) { - if (skip_if_not_supported) { - fprintf(stderr, - "Erase successful, but not automatically formatting.\n"); - fprintf(stderr, - "Can't determine partition type.\n"); - return 0; - } - fprintf(stderr,"FAILED (%s)\n", fb_get_error()); - return status; - } - - for (i = 0; i < ARRAY_SIZE(generators); i++) { - if (!strncmp(generators[i].fs_type, response, FB_RESPONSE_SZ)) { - generator = &generators[i]; - break; - } - } - if (!generator) { - if (skip_if_not_supported) { - fprintf(stderr, - "Erase successful, but not automatically formatting.\n"); - fprintf(stderr, - "File system type %s not supported.\n", response); - return 0; - } - fprintf(stderr,"Formatting is not supported for filesystem with type '%s'.\n", - response); - return -1; - } - - status = fb_getvar(usb, response, "partition-size:%s", partition); - if (status) { - if (skip_if_not_supported) { - fprintf(stderr, - "Erase successful, but not automatically formatting.\n"); - fprintf(stderr, "Unable to get partition size\n."); - return 0; - } - fprintf(stderr,"FAILED (%s)\n", fb_get_error()); - return status; - } - image.partition_size = strtoll(response, (char **)NULL, 16); - - generator->generate(&image); - if (!image.buffer) { - fprintf(stderr,"Cannot generate image.\n"); - return -1; - } - - // Following piece of code is similar to fb_queue_flash() but executes - // actions directly without queuing - fprintf(stderr, "sending '%s' (%lli KB)...\n", partition, image.image_size/1024); - status = fb_download_data(usb, image.buffer, image.image_size); - if (status) goto cleanup; - - fprintf(stderr, "writing '%s'...\n", partition); - snprintf(cmd, sizeof(cmd), "flash:%s", partition); - status = fb_command(usb, cmd); - if (status) goto cleanup; - -cleanup: - generator->cleanup(&image); - - return status; -} - -void fb_queue_format(const char *partition, int skip_if_not_supported) -{ - Action *a; - - a = queue_action(OP_FORMAT, partition); - a->data = (void*)skip_if_not_supported; - a->msg = mkmsg("formatting '%s' partition", partition); -} - void fb_queue_flash(const char *ptn, void *data, unsigned sz) { Action *a; @@ -573,6 +356,11 @@ void fb_queue_notice(const char *notice) a->data = (void*) notice; } +void fb_queue_wait_for_disconnect(void) +{ + queue_action(OP_WAIT_FOR_DISCONNECT, ""); +} + int fb_execute_queue(usb_handle *usb) { Action *a; @@ -606,14 +394,12 @@ int fb_execute_queue(usb_handle *usb) if (status) break; } else if (a->op == OP_NOTICE) { fprintf(stderr,"%s\n",(char*)a->data); - } else if (a->op == OP_FORMAT) { - status = fb_format(a, usb, (int)a->data); - status = a->func(a, status, status ? fb_get_error() : ""); - if (status) break; } else if (a->op == OP_DOWNLOAD_SPARSE) { status = fb_download_data_sparse(usb, a->data); status = a->func(a, status, status ? fb_get_error() : ""); if (status) break; + } else if (a->op == OP_WAIT_FOR_DISCONNECT) { + usb_wait_for_disconnect(usb); } else { die("bogus action"); } diff --git a/fastboot/fastboot.c b/fastboot/fastboot.c index f186c93..4fd1a8e 100644 --- a/fastboot/fastboot.c +++ b/fastboot/fastboot.c @@ -30,7 +30,6 @@ #include <stdio.h> #include <stdlib.h> -#include <stdarg.h> #include <stdbool.h> #include <stdint.h> #include <string.h> @@ -50,6 +49,7 @@ #include <zipfile/zipfile.h> #include "fastboot.h" +#include "fs.h" #ifndef O_BINARY #define O_BINARY 0 @@ -106,17 +106,6 @@ static struct { {"system.img", "system.sig", "system", false}, }; -void die(const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - fprintf(stderr,"error: "); - vfprintf(stderr, fmt, ap); - fprintf(stderr,"\n"); - va_end(ap); - exit(1); -} - void get_my_path(char *path); char *find_item(const char *item, const char *product) @@ -503,7 +492,13 @@ static int setup_requirement_line(char *name) for(n = 0; n < count; n++) { out[n] = strdup(strip(val[n])); - if (out[n] == 0) return -1; + if (out[n] == 0) { + for(size_t i = 0; i < n; ++i) { + free((char*) out[i]); + } + free(out); + return -1; + } } fb_queue_require(prod, name, invert, n, out); @@ -628,10 +623,13 @@ static int load_buf_fd(usb_handle *usb, int fd, void *data; int64_t limit; + sz64 = file_size(fd); if (sz64 < 0) { return -1; } + + lseek(fd, 0, SEEK_SET); limit = get_sparse_limit(usb, sz64); if (limit) { struct sparse_file **s = load_sparse_files(fd, limit); @@ -878,6 +876,73 @@ static int64_t parse_num(const char *arg) return num; } +void fb_perform_format(const char *partition, int skip_if_not_supported) +{ + char pType[FB_RESPONSE_SZ + 1], pSize[FB_RESPONSE_SZ + 1]; + unsigned int limit = INT_MAX; + struct fastboot_buffer buf; + const char *errMsg = NULL; + const struct fs_generator *gen; + uint64_t pSz; + int status; + int fd; + + if (target_sparse_limit > 0 && target_sparse_limit < limit) + limit = target_sparse_limit; + if (sparse_limit > 0 && sparse_limit < limit) + limit = sparse_limit; + + status = fb_getvar(usb, pType, "partition-type:%s", partition); + if (status) { + errMsg = "Can't determine partition type.\n"; + goto failed; + } + + status = fb_getvar(usb, pSize, "partition-size:%s", partition); + if (status) { + errMsg = "Unable to get partition size\n"; + goto failed; + } + + gen = fs_get_generator(pType); + if (!gen) { + if (skip_if_not_supported) { + fprintf(stderr, "Erase successful, but not automatically formatting.\n"); + fprintf(stderr, "File system type %s not supported.\n", pType); + return; + } + fprintf(stderr, "Formatting is not supported for filesystem with type '%s'.\n", pType); + return; + } + + pSz = strtoll(pSize, (char **)NULL, 16); + + fd = fileno(tmpfile()); + if (fs_generator_generate(gen, fd, pSz)) { + close(fd); + fprintf(stderr, "Cannot generate image.\n"); + return; + } + + if (load_buf_fd(usb, fd, &buf)) { + fprintf(stderr, "Cannot read image.\n"); + close(fd); + return; + } + flash_buf(partition, &buf); + + return; + + +failed: + if (skip_if_not_supported) { + fprintf(stderr, "Erase successful, but not automatically formatting.\n"); + if (errMsg) + fprintf(stderr, "%s", errMsg); + } + fprintf(stderr,"FAILED (%s)\n", fb_get_error()); +} + int main(int argc, char **argv) { int wants_wipe = 0; @@ -1006,7 +1071,7 @@ int main(int argc, char **argv) if (erase_first && needs_erase(argv[1])) { fb_queue_erase(argv[1]); } - fb_queue_format(argv[1], 0); + fb_perform_format(argv[1], 0); skip(2); } else if(!strcmp(*argv, "signature")) { require(2); @@ -1094,14 +1159,15 @@ int main(int argc, char **argv) if (wants_wipe) { fb_queue_erase("userdata"); - fb_queue_format("userdata", 1); + fb_perform_format("userdata", 1); fb_queue_erase("cache"); - fb_queue_format("cache", 1); + fb_perform_format("cache", 1); } if (wants_reboot) { fb_queue_reboot(); } else if (wants_reboot_bootloader) { fb_queue_command("reboot-bootloader", "rebooting into bootloader"); + fb_queue_wait_for_disconnect(); } if (fb_queue_is_empty()) diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h index c1b2964..c510a36 100644 --- a/fastboot/fastboot.h +++ b/fastboot/fastboot.h @@ -49,7 +49,7 @@ 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); +void fb_queue_format(const char *ptn, int skip_if_not_supported, unsigned int max_chunk_sz); void fb_queue_require(const char *prod, const char *var, int invert, unsigned nvalues, const char **value); void fb_queue_display(const char *var, const char *prettyname); @@ -58,10 +58,13 @@ void fb_queue_reboot(void); 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); +void fb_queue_wait_for_disconnect(void); int fb_execute_queue(usb_handle *usb); int fb_queue_is_empty(void); /* util stuff */ +double now(); +char *mkmsg(const char *fmt, ...); void die(const char *fmt, ...); /* Current product */ diff --git a/fastboot/fs.c b/fastboot/fs.c new file mode 100644 index 0000000..6a1f9e6 --- /dev/null +++ b/fastboot/fs.c @@ -0,0 +1,56 @@ +#include "fastboot.h" +#include "make_ext4fs.h" +#include "fs.h" + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <stdbool.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sparse/sparse.h> +#include <unistd.h> + +#ifdef USE_MINGW +#include <fcntl.h> +#else +#include <sys/mman.h> +#endif + + + +static int generate_ext4_image(int fd, long long partSize) +{ + make_ext4fs_sparse_fd(fd, partSize, NULL, NULL); + + return 0; +} + +static const struct fs_generator { + + char *fs_type; //must match what fastboot reports for partition type + int (*generate)(int fd, long long partSize); //returns 0 or error value + +} generators[] = { + + { "ext4", generate_ext4_image} + +}; + +const struct fs_generator* fs_get_generator(const char* name) +{ + unsigned i; + + for (i = 0; i < sizeof(generators) / sizeof(*generators); i++) + if (!strcmp(generators[i].fs_type, name)) + return generators + i; + + return NULL; +} + +int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize) +{ + return gen->generate(tmpFileNo, partSize); +} diff --git a/fastboot/fs.h b/fastboot/fs.h new file mode 100644 index 0000000..65b9555 --- /dev/null +++ b/fastboot/fs.h @@ -0,0 +1,12 @@ +#ifndef _FS_H_ +#define _FH_H_ + +#include <stdint.h> + +struct fs_generator; + +const struct fs_generator* fs_get_generator(const char* name); +int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize); + +#endif + diff --git a/fastboot/usb.h b/fastboot/usb.h index d504ee2..17cf0a9 100644 --- a/fastboot/usb.h +++ b/fastboot/usb.h @@ -62,6 +62,6 @@ usb_handle *usb_open(ifc_match_func callback); int usb_close(usb_handle *h); int usb_read(usb_handle *h, void *_data, int len); int usb_write(usb_handle *h, const void *_data, int len); - +int usb_wait_for_disconnect(usb_handle *h); #endif diff --git a/fastboot/usb_linux.c b/fastboot/usb_linux.c index b7a9ca3..f2ce226 100644 --- a/fastboot/usb_linux.c +++ b/fastboot/usb_linux.c @@ -50,10 +50,17 @@ #endif #include <asm/byteorder.h> +#include "fastboot.h" #include "usb.h" #define MAX_RETRIES 5 +/* Timeout in seconds for usb_wait_for_disconnect. + * It doesn't usually take long for a device to disconnect (almost always + * under 2 seconds) but we'll time out after 3 seconds just in case. + */ +#define WAIT_FOR_DISCONNECT_TIMEOUT 3 + #ifdef TRACE_USB #define DBG1(x...) fprintf(stderr, x) #define DBG(x...) fprintf(stderr, x) @@ -75,10 +82,18 @@ struct usb_handle unsigned char ep_out; }; +/* True if name isn't a valid name for a USB device in /sys/bus/usb/devices. + * Device names are made up of numbers, dots, and dashes, e.g., '7-1.5'. + * We reject interfaces (e.g., '7-1.5:1.0') and host controllers (e.g. 'usb1'). + * The name must also start with a digit, to disallow '.' and '..' + */ static inline int badname(const char *name) { - while(*name) { - if(!isdigit(*name++)) return 1; + if (!isdigit(*name)) + return 1; + while(*++name) { + if(!isdigit(*name) && *name != '.' && *name != '-') + return 1; } return 0; } @@ -95,7 +110,8 @@ static int check(void *_desc, int len, unsigned type, int size) return 0; } -static int filter_usb_device(int fd, char *ptr, int len, int writable, +static int filter_usb_device(char* sysfs_name, + char *ptr, int len, int writable, ifc_match_func callback, int *ept_in_id, int *ept_out_id, int *ifc_id) { @@ -131,69 +147,35 @@ static int filter_usb_device(int fd, char *ptr, int len, int writable, info.dev_protocol = dev->bDeviceProtocol; info.writable = writable; - // read device serial number (if there is one) - info.serial_number[0] = 0; - if (dev->iSerialNumber) { - struct usbdevfs_ctrltransfer ctrl; - // Keep it short enough because some bootloaders are borked if the URB len is > 255 - // 128 is too big by 1. - __u16 buffer[127]; - - memset(buffer, 0, sizeof(buffer)); - - ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE; - ctrl.bRequest = USB_REQ_GET_DESCRIPTOR; - ctrl.wValue = (USB_DT_STRING << 8) | dev->iSerialNumber; - //language ID (en-us) for serial number string - ctrl.wIndex = 0x0409; - ctrl.wLength = sizeof(buffer); - ctrl.data = buffer; - ctrl.timeout = 50; - - result = ioctl(fd, USBDEVFS_CONTROL, &ctrl); - if (result > 0) { - int i; - // skip first word, and copy the rest to the serial string, changing shorts to bytes. - result /= 2; - for (i = 1; i < result; i++) - info.serial_number[i - 1] = buffer[i]; - info.serial_number[i - 1] = 0; - } - } + snprintf(info.device_path, sizeof(info.device_path), "usb:%s", sysfs_name); - /* 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). + /* Read device serial number (if there is one). + * We read the serial number from sysfs, since it's faster and more + * reliable than issuing a control pipe read, and also won't + * cause problems for devices which don't like getting descriptor + * requests while they're in the middle of flashing. */ - 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); + info.serial_number[0] = '\0'; + if (dev->iSerialNumber) { + char path[80]; + int fd; + + snprintf(path, sizeof(path), + "/sys/bus/usb/devices/%s/serial", sysfs_name); + path[sizeof(path) - 1] = '\0'; + + fd = open(path, O_RDONLY); + if (fd >= 0) { + int chars_read = read(fd, info.serial_number, + sizeof(info.serial_number) - 1); + close(fd); + + if (chars_read <= 0) + info.serial_number[0] = '\0'; + else if (info.serial_number[chars_read - 1] == '\n') { + // strip trailing newline + info.serial_number[chars_read - 1] = '\0'; + } } } @@ -241,14 +223,73 @@ static int filter_usb_device(int fd, char *ptr, int len, int writable, return -1; } +static int read_sysfs_string(const char *sysfs_name, const char *sysfs_node, + char* buf, int bufsize) +{ + char path[80]; + int fd, n; + + snprintf(path, sizeof(path), + "/sys/bus/usb/devices/%s/%s", sysfs_name, sysfs_node); + path[sizeof(path) - 1] = '\0'; + + fd = open(path, O_RDONLY); + if (fd < 0) + return -1; + + n = read(fd, buf, bufsize - 1); + close(fd); + + if (n < 0) + return -1; + + buf[n] = '\0'; + + return n; +} + +static int read_sysfs_number(const char *sysfs_name, const char *sysfs_node) +{ + char buf[16]; + int value; + + if (read_sysfs_string(sysfs_name, sysfs_node, buf, sizeof(buf)) < 0) + return -1; + + if (sscanf(buf, "%d", &value) != 1) + return -1; + + return value; +} + +/* Given the name of a USB device in sysfs, get the name for the same + * device in devfs. Returns 0 for success, -1 for failure. + */ +static int convert_to_devfs_name(const char* sysfs_name, + char* devname, int devname_size) +{ + int busnum, devnum; + + busnum = read_sysfs_number(sysfs_name, "busnum"); + if (busnum < 0) + return -1; + + devnum = read_sysfs_number(sysfs_name, "devnum"); + if (devnum < 0) + return -1; + + snprintf(devname, devname_size, "/dev/bus/usb/%03d/%03d", busnum, devnum); + return 0; +} + static usb_handle *find_usb_device(const char *base, ifc_match_func callback) { usb_handle *usb = 0; - char busname[64], devname[64]; + char devname[64]; char desc[1024]; int n, in, out, ifc; - DIR *busdir, *devdir; + DIR *busdir; struct dirent *de; int fd; int writable; @@ -259,15 +300,7 @@ static usb_handle *find_usb_device(const char *base, ifc_match_func callback) while((de = readdir(busdir)) && (usb == 0)) { if(badname(de->d_name)) continue; - sprintf(busname, "%s/%s", base, de->d_name); - devdir = opendir(busname); - if(devdir == 0) continue; - -// DBG("[ scanning %s ]\n", busname); - while((de = readdir(devdir)) && (usb == 0)) { - - if(badname(de->d_name)) continue; - sprintf(devname, "%s/%s", busname, de->d_name); + if(!convert_to_devfs_name(de->d_name, devname, sizeof(devname))) { // DBG("[ scanning %s ]\n", devname); writable = 1; @@ -282,7 +315,7 @@ static usb_handle *find_usb_device(const char *base, ifc_match_func callback) n = read(fd, desc, sizeof(desc)); - if(filter_usb_device(fd, desc, n, writable, callback, + if(filter_usb_device(de->d_name, desc, n, writable, callback, &in, &out, &ifc) == 0) { usb = calloc(1, sizeof(usb_handle)); strcpy(usb->fname, devname); @@ -301,7 +334,6 @@ static usb_handle *find_usb_device(const char *base, ifc_match_func callback) close(fd); } } - closedir(devdir); } closedir(busdir); @@ -315,26 +347,11 @@ int usb_write(usb_handle *h, const void *_data, int len) struct usbdevfs_bulktransfer bulk; int n; - if(h->ep_out == 0) { + if(h->ep_out == 0 || h->desc == -1) { return -1; } - if(len == 0) { - bulk.ep = h->ep_out; - bulk.len = 0; - bulk.data = data; - bulk.timeout = 0; - - n = ioctl(h->desc, USBDEVFS_BULK, &bulk); - if(n != 0) { - fprintf(stderr,"ERROR: n = %d, errno = %d (%s)\n", - n, errno, strerror(errno)); - return -1; - } - return 0; - } - - while(len > 0) { + do { int xfer; xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len; @@ -353,7 +370,7 @@ int usb_write(usb_handle *h, const void *_data, int len) count += xfer; len -= xfer; data += xfer; - } + } while(len > 0); return count; } @@ -365,7 +382,7 @@ int usb_read(usb_handle *h, void *_data, int len) struct usbdevfs_bulktransfer bulk; int n, retry; - if(h->ep_in == 0) { + if(h->ep_in == 0 || h->desc == -1) { return -1; } @@ -431,5 +448,20 @@ int usb_close(usb_handle *h) usb_handle *usb_open(ifc_match_func callback) { - return find_usb_device("/dev/bus/usb", callback); + return find_usb_device("/sys/bus/usb/devices", callback); +} + +/* Wait for the system to notice the device is gone, so that a subsequent + * fastboot command won't try to access the device before it's rebooted. + * Returns 0 for success, -1 for timeout. + */ +int usb_wait_for_disconnect(usb_handle *usb) +{ + double deadline = now() + WAIT_FOR_DISCONNECT_TIMEOUT; + while (now() < deadline) { + if (access(usb->fname, F_OK)) + return 0; + usleep(50000); + } + return -1; } diff --git a/fastboot/usb_osx.c b/fastboot/usb_osx.c index 1548ba8..0f55e0d 100644 --- a/fastboot/usb_osx.c +++ b/fastboot/usb_osx.c @@ -467,6 +467,11 @@ int usb_close(usb_handle *h) { return 0; } +int usb_wait_for_disconnect(usb_handle *usb) { + /* TODO: Punt for now */ + return 0; +} + int usb_read(usb_handle *h, void *data, int len) { IOReturn result; UInt32 numBytes = len; diff --git a/fastboot/usb_windows.c b/fastboot/usb_windows.c index 7aa36b2..07f7be2 100644 --- a/fastboot/usb_windows.c +++ b/fastboot/usb_windows.c @@ -269,6 +269,11 @@ int usb_close(usb_handle* handle) { return 0; } +int usb_wait_for_disconnect(usb_handle *usb) { + /* TODO: Punt for now */ + return 0; +} + int recognized_device(usb_handle* handle, ifc_match_func callback) { struct usb_ifc_info info; USB_DEVICE_DESCRIPTOR device_desc; diff --git a/fastboot/util.c b/fastboot/util.c new file mode 100644 index 0000000..f2bbd34 --- /dev/null +++ b/fastboot/util.c @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <sys/time.h> + +#include "fastboot.h" + +double now() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return (double)tv.tv_sec + (double)tv.tv_usec / 1000000; +} + +char *mkmsg(const char *fmt, ...) +{ + char buf[256]; + char *s; + va_list ap; + + va_start(ap, fmt); + vsprintf(buf, fmt, ap); + va_end(ap); + + s = strdup(buf); + if (s == 0) die("out of memory"); + return s; +} + +void die(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr,"error: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr,"\n"); + va_end(ap); + exit(1); +} diff --git a/fastbootd/Android.mk b/fastbootd/Android.mk index 76b28e2..0f32dbf 100644 --- a/fastbootd/Android.mk +++ b/fastbootd/Android.mk @@ -16,23 +16,89 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) +LOCAL_C_INCLUDES := \ + external/openssl/include \ + external/mdnsresponder/mDNSShared \ + $(LOCAL_PATH)/include \ + external/zlib/ \ + LOCAL_SRC_FILES := \ config.c \ commands.c \ + commands/boot.c \ + commands/flash.c \ + commands/partitions.c \ + commands/virtual_partitions.c \ fastbootd.c \ protocol.c \ + network_discovery.c \ + socket_client.c \ + secure.c \ transport.c \ - usb_linux_client.c + transport_socket.c \ + trigger.c \ + usb_linux_client.c \ + utils.c \ LOCAL_MODULE := fastbootd LOCAL_MODULE_TAGS := optional +LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter -DFLASH_CERT +LOCAL_LDFLAGS := -ldl + +LOCAL_SHARED_LIBRARIES := \ + libhardware \ + libcrypto \ + libhardware_legacy \ + libmdnssd + +LOCAL_STATIC_LIBRARIES := \ + libsparse_static \ + libc \ + libcutils \ + libz + +#LOCAL_FORCE_STATIC_EXECUTABLE := true + +include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) +LOCAL_C_INCLUDES := \ + external/zlib/ + +LOCAL_SRC_FILES := \ + commands/partitions.c \ + other/gptedit.c \ + utils.c + +LOCAL_MODULE := gptedit +LOCAL_MODULE_TAGS := optional LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter LOCAL_STATIC_LIBRARIES := \ libsparse_static \ libc \ - libcutils + libcutils \ + libz LOCAL_FORCE_STATIC_EXECUTABLE := true include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/include \ + +LOCAL_STATIC_LIBRARIES := \ + $(EXTRA_STATIC_LIBS) \ + libcutils + +LOCAL_SRC_FILES := \ + other/vendor_trigger.c + +LOCAL_MODULE := libvendortrigger.default +LOCAL_MODULE_TAGS := optional +LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter + + +include $(BUILD_SHARED_LIBRARY) diff --git a/fastbootd/commands.c b/fastbootd/commands.c index 252f655..063e1a6 100644 --- a/fastbootd/commands.c +++ b/fastbootd/commands.c @@ -29,117 +29,308 @@ * SUCH DAMAGE. */ +#include <inttypes.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <unistd.h> +#include <sys/reboot.h> +#include <fcntl.h> #include "bootimg.h" +#include "commands/boot.h" +#include "commands/flash.h" +#include "commands/partitions.h" +#include "commands/virtual_partitions.h" #include "debug.h" #include "protocol.h" +#include "trigger.h" +#include "utils.h" + +#define ATAGS_LOCATION "/proc/atags" static void cmd_boot(struct protocol_handle *phandle, const char *arg) { -#if 0 + int sz, atags_sz, new_atags_sz; + int rv; unsigned kernel_actual; unsigned ramdisk_actual; - static struct boot_img_hdr hdr; - char *ptr = ((char*) data); + unsigned second_actual; + void *kernel_ptr; + void *ramdisk_ptr; + void *second_ptr; + struct boot_img_hdr *hdr; + char *ptr = NULL; + char *atags_ptr = NULL; + char *new_atags = NULL; + int data_fd = 0; + + D(DEBUG, "cmd_boot %s\n", arg); - if (sz < sizeof(hdr)) { - fastboot_fail(phandle, "invalid bootimage header"); + if (phandle->download_fd < 0) { + fastboot_fail(phandle, "no kernel file"); + return; + } + + atags_ptr = read_atags(ATAGS_LOCATION, &atags_sz); + if (atags_ptr == NULL) { + fastboot_fail(phandle, "atags read error"); + goto error; + } + + // TODO: With cms we can also verify partition name included as + // cms signed attribute + if (flash_validate_certificate(phandle->download_fd, &data_fd) != 1) { + fastboot_fail(phandle, "Access forbiden you need the certificate"); return; } - memcpy(&hdr, data, sizeof(hdr)); + sz = get_file_size(data_fd); + + ptr = (char *) mmap(NULL, sz, PROT_READ, + MAP_POPULATE | MAP_PRIVATE, data_fd, 0); + + hdr = (struct boot_img_hdr *) ptr; + + if (ptr == MAP_FAILED) { + fastboot_fail(phandle, "internal fastbootd error"); + goto error; + } + + if ((size_t) sz < sizeof(*hdr)) { + fastboot_fail(phandle, "invalid bootimage header"); + goto error; + } + + kernel_actual = ROUND_TO_PAGE(hdr->kernel_size, hdr->page_size); + ramdisk_actual = ROUND_TO_PAGE(hdr->ramdisk_size, hdr->page_size); + second_actual = ROUND_TO_PAGE(hdr->second_size, hdr->page_size); - /* ensure commandline is terminated */ - hdr.cmdline[BOOT_ARGS_SIZE-1] = 0; + new_atags = (char *) create_atags((unsigned *) atags_ptr, atags_sz, hdr, &new_atags_sz); - kernel_actual = ROUND_TO_PAGE(hdr.kernel_size); - ramdisk_actual = ROUND_TO_PAGE(hdr.ramdisk_size); + if (new_atags == NULL) { + fastboot_fail(phandle, "atags generate error"); + goto error; + } + if (new_atags_sz > 0x4000) { + fastboot_fail(phandle, "atags file to large"); + goto error; + } - if (2048 + kernel_actual + ramdisk_actual < sz) { + if ((int) (hdr->page_size + kernel_actual + ramdisk_actual) < sz) { fastboot_fail(phandle, "incomplete bootimage"); - return; + goto error; } - /*memmove((void*) KERNEL_ADDR, ptr + 2048, hdr.kernel_size); - memmove((void*) RAMDISK_ADDR, ptr + 2048 + kernel_actual, hdr.ramdisk_size);*/ + kernel_ptr = (void *)((unsigned) ptr + hdr->page_size); + ramdisk_ptr = (void *)((unsigned) kernel_ptr + kernel_actual); + second_ptr = (void *)((unsigned) ramdisk_ptr + ramdisk_actual); + + D(INFO, "preparing to boot"); + // Prepares boot physical address. Addresses from header are ignored + rv = prepare_boot_linux(hdr->kernel_addr, kernel_ptr, kernel_actual, + hdr->ramdisk_addr, ramdisk_ptr, ramdisk_actual, + hdr->second_addr, second_ptr, second_actual, + hdr->tags_addr, new_atags, ROUND_TO_PAGE(new_atags_sz, hdr->page_size)); + if (rv < 0) { + fastboot_fail(phandle, "kexec prepare failed"); + goto error; + } fastboot_okay(phandle, ""); - udc_stop(); + free(atags_ptr); + munmap(ptr, sz); + free(new_atags); + close(data_fd); + + D(INFO, "Kexec going to reboot"); + reboot(LINUX_REBOOT_CMD_KEXEC); + + fastboot_fail(phandle, "reboot error"); + + return; + +error: + + if (atags_ptr != NULL) + free(atags_ptr); + if (ptr != NULL) + munmap(ptr, sz); - /*boot_linux((void*) KERNEL_ADDR, (void*) TAGS_ADDR, - (const char*) hdr.cmdline, LINUX_MACHTYPE, - (void*) RAMDISK_ADDR, hdr.ramdisk_size);*/ -#endif } static void cmd_erase(struct protocol_handle *phandle, const char *arg) { -#if 0 - struct ptentry *ptn; - struct ptable *ptable; + int partition_fd; + char path[PATH_MAX]; + D(DEBUG, "cmd_erase %s\n", arg); - ptable = flash_get_ptable(); - if (ptable == NULL) { + if (flash_find_entry(arg, path, PATH_MAX)) { fastboot_fail(phandle, "partition table doesn't exist"); return; } - ptn = ptable_find(ptable, arg); - if (ptn == NULL) { - fastboot_fail(phandle, "unknown partition name"); + if (path == NULL) { + fastboot_fail(phandle, "Couldn't find partition"); + return; + } + + partition_fd = flash_get_partiton(path); + if (partition_fd < 0) { + fastboot_fail(phandle, "partiton file does not exists"); + } + + if (flash_erase(partition_fd)) { + fastboot_fail(phandle, "failed to erase partition"); + flash_close(partition_fd); return; } - if (flash_erase(ptn)) { + if (flash_close(partition_fd) < 0) { + D(ERR, "could not close device %s", strerror(errno)); fastboot_fail(phandle, "failed to erase partition"); return; } fastboot_okay(phandle, ""); -#endif +} + +static int GPT_header_location() { + const char *location_str = fastboot_getvar("gpt_sector"); + char *str; + int location; + + if (!strcmp("", location_str)) { + D(INFO, "GPT location not specified using second sector"); + return 1; + } + else { + location = strtoul(location_str, &str, 10); + D(INFO, "GPT location specified as %d", location); + + if (*str != '\0') + return -1; + + return location - 1; + } +} + +static void cmd_gpt_layout(struct protocol_handle *phandle, const char *arg) { + struct GPT_entry_table *oldtable; + int location; + struct GPT_content content; + const char *device; + device = fastboot_getvar("blockdev"); + + if (!strcmp(device, "")) { + fastboot_fail(phandle, "blockdev not defined in config file"); + return; + } + + //TODO: add same verification as in cmd_flash + if (phandle->download_fd < 0) { + fastboot_fail(phandle, "no layout file"); + return; + } + + location = GPT_header_location(); + oldtable = GPT_get_device(device, location); + + GPT_default_content(&content, oldtable); + if (oldtable == NULL) + D(WARN, "Could not get old gpt table"); + else + GPT_release_device(oldtable); + + if (!GPT_parse_file(phandle->download_fd, &content)) { + fastboot_fail(phandle, "Could not parse partition config file"); + return; + } + + if (trigger_gpt_layout(&content)) { + fastboot_fail(phandle, "Vendor forbids this opperation"); + GPT_release_content(&content); + return; + } + + if (!GPT_write_content(device, &content)) { + fastboot_fail(phandle, "Unable to write gpt file"); + GPT_release_content(&content); + return; + } + + GPT_release_content(&content); + fastboot_okay(phandle, ""); } static void cmd_flash(struct protocol_handle *phandle, const char *arg) { -#if 0 - struct ptentry *ptn; - struct ptable *ptable; - unsigned extra = 0; + int partition; + uint64_t sz; + char data[BOOT_MAGIC_SIZE]; + char path[PATH_MAX]; + ssize_t header_sz = 0; + int data_fd = 0; + + D(DEBUG, "cmd_flash %s\n", arg); + + if (try_handle_virtual_partition(phandle, arg)) { + return; + } + + if (phandle->download_fd < 0) { + fastboot_fail(phandle, "no kernel file"); + return; + } - ptable = flash_get_ptable(); - if (ptable == NULL) { + if (flash_find_entry(arg, path, PATH_MAX)) { fastboot_fail(phandle, "partition table doesn't exist"); return; } - ptn = ptable_find(ptable, arg); - if (ptn == NULL) { - fastboot_fail(phandle, "unknown partition name"); + if (flash_validate_certificate(phandle->download_fd, &data_fd) != 1) { + fastboot_fail(phandle, "Access forbiden you need certificate"); return; } - if (!strcmp(ptn->name, "boot") || !strcmp(ptn->name, "recovery")) { + // TODO: Maybe its goot idea to check whether the partition is bootable + if (!strcmp(arg, "boot") || !strcmp(arg, "recovery")) { + if (read_data_once(data_fd, data, BOOT_MAGIC_SIZE) < BOOT_MAGIC_SIZE) { + fastboot_fail(phandle, "incoming data read error, cannot read boot header"); + return; + } if (memcmp((void *)data, BOOT_MAGIC, BOOT_MAGIC_SIZE)) { fastboot_fail(phandle, "image is not a boot image"); return; } } - if (!strcmp(ptn->name, "system") || !strcmp(ptn->name, "userdata")) - extra = 64; - else - sz = ROUND_TO_PAGE(sz); + partition = flash_get_partiton(path); - D(INFO, "writing %d bytes to '%s'\n", sz, ptn->name); - if (flash_write(ptn, extra, data, sz)) { + sz = get_file_size64(data_fd); + + sz -= header_sz; + + if (sz > get_file_size64(partition)) { + flash_close(partition); + D(WARN, "size of file too large"); + fastboot_fail(phandle, "size of file too large"); + return; + } + + D(INFO, "writing %"PRId64" bytes to '%s'\n", sz, arg); + + if (flash_write(partition, phandle->download_fd, sz, header_sz)) { fastboot_fail(phandle, "flash write failure"); return; } - D(INFO, "partition '%s' updated\n", ptn->name); -#endif + D(INFO, "partition '%s' updated\n", arg); + + flash_close(partition); + close(data_fd); + fastboot_okay(phandle, ""); } @@ -184,7 +375,6 @@ static void cmd_download(struct protocol_handle *phandle, const char *arg) phandle->download_fd = protocol_handle_download(phandle, len); if (phandle->download_fd < 0) { - //handle->state = STATE_ERROR; fastboot_fail(phandle, "download failed"); return; } @@ -192,14 +382,27 @@ static void cmd_download(struct protocol_handle *phandle, const char *arg) fastboot_okay(phandle, ""); } +static void cmd_oem(struct protocol_handle *phandle, const char *arg) { + const char *response = ""; + + //TODO: Maybe it should get download descriptor also + if (trigger_oem_cmd(arg, &response)) + fastboot_fail(phandle, response); + else + fastboot_okay(phandle, response); +} + void commands_init() { + virtual_partition_register("partition-table", cmd_gpt_layout); + fastboot_register("boot", cmd_boot); fastboot_register("erase:", cmd_erase); fastboot_register("flash:", cmd_flash); fastboot_register("continue", cmd_continue); fastboot_register("getvar:", cmd_getvar); fastboot_register("download:", cmd_download); + fastboot_register("oem", cmd_oem); //fastboot_publish("version", "0.5"); //fastboot_publish("product", "swordfish"); //fastboot_publish("kernel", "lk"); diff --git a/fastbootd/commands/boot.c b/fastbootd/commands/boot.c new file mode 100644 index 0000000..8da9a28 --- /dev/null +++ b/fastbootd/commands/boot.c @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2009-2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/syscall.h> +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> + +#include "boot.h" +#include "debug.h" +#include "utils.h" +#include "bootimg.h" + + +#define KEXEC_ARM_ATAGS_OFFSET 0x1000 +#define KEXEC_ARM_ZIMAGE_OFFSET 0x8000 + +#define MEMORY_SIZE 0x0800000 +#define START_ADDRESS 0x44000000 +#define KERNEL_START (START_ADDRESS + KEXEC_ARM_ZIMAGE_OFFSET) + +#define ATAG_NONE_TYPE 0x00000000 +#define ATAG_CORE_TYPE 0x54410001 +#define ATAG_RAMDISK_TYPE 0x54410004 +#define ATAG_INITRD2_TYPE 0x54420005 +#define ATAG_CMDLINE_TYPE 0x54410009 + +#define MAX_ATAG_SIZE 0x4000 + +struct atag_info { + unsigned size; + unsigned type; +}; + +struct atag_initrd2 { + unsigned start; + unsigned size; +}; + +struct atag_cmdline { + char cmdline[0]; +}; + +struct atag { + struct atag_info info; + union { + struct atag_initrd2 initrd2; + struct atag_cmdline cmdline; + } data; +}; + + +long kexec_load(unsigned int entry, unsigned long nr_segments, + struct kexec_segment *segment, unsigned long flags) { + return syscall(__NR_kexec_load, entry, nr_segments, segment, flags); +} + +/* + * Prepares arguments for kexec + * Kernel address is not set into kernel_phys + * Ramdisk is set to position relative to kernel + */ +int prepare_boot_linux(unsigned kernel_phys, void *kernel_addr, int kernel_size, + unsigned ramdisk_phys, void *ramdisk_addr, int ramdisk_size, + unsigned second_phys, void *second_addr, int second_size, + unsigned atags_phys, void *atags_addr, int atags_size) { + struct kexec_segment segment[4]; + int segment_count = 2; + unsigned entry = START_ADDRESS + KEXEC_ARM_ZIMAGE_OFFSET; + int rv; + int page_size = getpagesize(); + + segment[0].buf = kernel_addr; + segment[0].bufsz = kernel_size; + segment[0].mem = (void *) KERNEL_START; + segment[0].memsz = ROUND_TO_PAGE(kernel_size, page_size); + + if (kernel_size > MEMORY_SIZE - KEXEC_ARM_ZIMAGE_OFFSET) { + D(INFO, "Kernel image too big"); + return -1; + } + + segment[1].buf = atags_addr; + segment[1].bufsz = atags_size; + segment[1].mem = (void *) (START_ADDRESS + KEXEC_ARM_ATAGS_OFFSET); + segment[1].memsz = ROUND_TO_PAGE(atags_size, page_size); + + D(INFO, "Ramdisk size is %d", ramdisk_size); + + if (ramdisk_size != 0) { + segment[segment_count].buf = ramdisk_addr; + segment[segment_count].bufsz = ramdisk_size; + segment[segment_count].mem = (void *) (KERNEL_START + ramdisk_phys - kernel_phys); + segment[segment_count].memsz = ROUND_TO_PAGE(ramdisk_phys, page_size); + ++segment_count; + } + + D(INFO, "Ramdisk size is %d", ramdisk_size); + if (second_size != 0) { + segment[segment_count].buf = second_addr; + segment[segment_count].bufsz = second_size; + segment[segment_count].mem = (void *) (KERNEL_START + second_phys - kernel_phys); + segment[segment_count].memsz = ROUND_TO_PAGE(second_size, page_size); + entry = second_phys; + ++segment_count; + } + + rv = kexec_load(entry, segment_count, segment, KEXEC_ARCH_DEFAULT); + + if (rv != 0) { + D(INFO, "Kexec_load returned non-zero exit code: %s\n", strerror(errno)); + return -1; + } + + return 1; + +} + +unsigned *create_atags(unsigned *atags_position, int atag_size, const struct boot_img_hdr *hdr, int *size) { + struct atag *current_tag = (struct atag *) atags_position; + unsigned *current_tag_raw = atags_position; + unsigned *new_atags = malloc(ROUND_TO_PAGE(atag_size + BOOT_ARGS_SIZE * sizeof(char), + hdr->page_size)); + //This pointer will point into the beggining of buffer free space + unsigned *natags_raw_buff = new_atags; + int new_atags_size = 0; + int current_size; + int cmdl_length; + + // copy tags from current atag file + while (current_tag->info.type != ATAG_NONE_TYPE) { + switch (current_tag->info.type) { + case ATAG_CMDLINE_TYPE: + case ATAG_RAMDISK_TYPE: + case ATAG_INITRD2_TYPE: break; + default: + memcpy((void *)natags_raw_buff, (void *)current_tag_raw, current_tag->info.size * sizeof(unsigned)); + natags_raw_buff += current_tag->info.size; + new_atags_size += current_tag->info.size; + } + + current_tag_raw += current_tag->info.size; + current_tag = (struct atag *) current_tag_raw; + + if (current_tag_raw >= atags_position + atag_size) { + D(ERR, "Critical error in atags"); + return NULL; + } + } + + // set INITRD2 tag + if (hdr->ramdisk_size > 0) { + current_size = (sizeof(struct atag_info) + sizeof(struct atag_initrd2)) / sizeof(unsigned); + *((struct atag *) natags_raw_buff) = (struct atag) { + .info = { + .size = current_size, + .type = ATAG_INITRD2_TYPE + }, + .data = { + .initrd2 = (struct atag_initrd2) { + .start = hdr->ramdisk_addr, + .size = hdr->ramdisk_size + } + } + }; + + new_atags_size += current_size; + natags_raw_buff += current_size; + } + + // set ATAG_CMDLINE + cmdl_length = strnlen((char *) hdr->cmdline, BOOT_ARGS_SIZE - 1); + current_size = sizeof(struct atag_info) + (1 + cmdl_length); + current_size = (current_size + sizeof(unsigned) - 1) / sizeof(unsigned); + *((struct atag *) natags_raw_buff) = (struct atag) { + .info = { + .size = current_size, + .type = ATAG_CMDLINE_TYPE + }, + }; + + //copy cmdline and ensure that there is null character + memcpy(((struct atag *) natags_raw_buff)->data.cmdline.cmdline, + (char *) hdr->cmdline, cmdl_length); + ((struct atag *) natags_raw_buff)->data.cmdline.cmdline[cmdl_length] = '\0'; + + new_atags_size += current_size; + natags_raw_buff += current_size; + + // set ATAG_NONE + *((struct atag *) natags_raw_buff) = (struct atag) { + .info = { + .size = 0, + .type = ATAG_NONE_TYPE + }, + }; + new_atags_size += sizeof(struct atag_info) / sizeof(unsigned); + natags_raw_buff += sizeof(struct atag_info) / sizeof(unsigned); + + *size = new_atags_size * sizeof(unsigned); + return new_atags; +} + +char *read_atags(const char * path, int *atags_sz) { + int afd = -1; + char *atags_ptr = NULL; + + afd = open(path, O_RDONLY); + if (afd < 0) { + D(ERR, "wrong atags file"); + return 0; + } + + atags_ptr = (char *) malloc(MAX_ATAG_SIZE); + if (atags_ptr == NULL) { + D(ERR, "insufficient memory"); + return 0; + } + + *atags_sz = read(afd, atags_ptr, MAX_ATAG_SIZE); + + close(afd); + return atags_ptr; +} + diff --git a/fastbootd/commands/boot.h b/fastbootd/commands/boot.h new file mode 100644 index 0000000..901c38a --- /dev/null +++ b/fastbootd/commands/boot.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2009-2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef __FASTBOOT_BOOT_H +#define __FASTBOOT_BOOT_H + +#include <sys/cdefs.h> +#include <linux/kexec.h> + +#include "bootimg.h" + +#define KEXEC_TYPE_DEFAULT 0 +#define KEXEC_TYPE_CRASH 1 + +int prepare_boot_linux(unsigned, void *, int, unsigned, void *, int, + unsigned, void *, int, unsigned, void *, int); +unsigned *create_atags(unsigned *, int, const struct boot_img_hdr *, int *); +long kexec_load(unsigned int, unsigned long, struct kexec_segment *, unsigned long); +char *read_atags(const char *, int *); + +#endif /* _SYS_KEXEC_H */ + diff --git a/fastbootd/commands/flash.c b/fastbootd/commands/flash.c new file mode 100644 index 0000000..1eb4d1b --- /dev/null +++ b/fastbootd/commands/flash.c @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2009-2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/stat.h> +#include <fcntl.h> +#include <inttypes.h> +#include <sys/mman.h> + +#include "flash.h" +#include "protocol.h" +#include "debug.h" +#include "utils.h" +#include "commands/partitions.h" + +#ifdef FLASH_CERT +#include "secure.h" +#endif + +#define ALLOWED_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-." +#define BUFFER_SIZE 1024 * 1024 +#define MIN(a, b) (a > b ? b : a) + + +int flash_find_entry(const char *name, char *out, size_t outlen) +{ +//TODO: Assumption: All the partitions has they unique name + + const char *path = fastboot_getvar("device-directory"); + size_t length; + if (strcmp(path, "") == 0) { + D(ERR, "device-directory: not defined in config file"); + return -1; + } + + length = strspn(name, ALLOWED_CHARS); + if (length != strlen(name)) { + D(ERR, "Not allowed char in name: %c", name[length]); + return -1; + } + + if (snprintf(out, outlen, "%s%s", path, name) >= (int) outlen) { + D(ERR, "Too long path to partition file"); + return -1; + } + + if (access(out, F_OK ) == -1) { + D(ERR, "could not find partition file %s", name); + return -1; + } + + return 0; +} + +int flash_erase(int fd) +{ + int64_t size; + size = get_block_device_size(fd); + D(DEBUG, "erase %"PRId64" data from %d\n", size, fd); + + return wipe_block_device(fd, size); +} + +int flash_write(int partition_fd, int data_fd, ssize_t size, ssize_t skip) +{ + ssize_t written = 0; + struct GPT_mapping input; + struct GPT_mapping output; + + while (written < size) { + int current_size = MIN(size - written, BUFFER_SIZE); + + if (gpt_mmap(&input, written + skip, current_size, data_fd)) { + D(ERR, "Error in writing data, unable to map data file %zd at %zd size %d", size, skip, current_size); + return -1; + } + if (gpt_mmap(&output, written, current_size, partition_fd)) { + D(ERR, "Error in writing data, unable to map output partition"); + return -1; + } + + memcpy(output.ptr, input.ptr, current_size); + + gpt_unmap(&input); + gpt_unmap(&output); + + written += current_size; + } + + return 0; +} + +#ifdef FLASH_CERT + +int flash_validate_certificate(int signed_fd, int *data_fd) { + int ret = 0; + const char *cert_path; + X509_STORE *store = NULL; + CMS_ContentInfo *content_info; + BIO *content; + + cert_path = fastboot_getvar("certificate-path"); + if (!strcmp(cert_path, "")) { + D(ERR, "could not find cert-key value in config file"); + goto finish; + } + + store = cert_store_from_path(cert_path); + if (store == NULL) { + D(ERR, "unable to create certification store"); + goto finish; + } + + if (cert_read(signed_fd, &content_info, &content)) { + D(ERR, "reading data failed"); + goto finish; + } + + ret = cert_verify(content, content_info, store, data_fd); + cert_release(content, content_info); + + return ret; + +finish: + if (store != NULL) + cert_release_store(store); + + return ret; +} + +#else +int flash_validate_certificate(int signed_fd, int *data_fd) { + return 1; +} +#endif diff --git a/fastbootd/commands/flash.h b/fastbootd/commands/flash.h new file mode 100644 index 0000000..5a64cab --- /dev/null +++ b/fastbootd/commands/flash.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2009-2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _FASTBOOTD_ERASE_H +#define _FASTBOOTD_ERASE_H + +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include "debug.h" + +int flash_find_entry(const char *, char *, size_t); +int flash_erase(int fd); + +static inline int flash_get_partiton(const char *path) { + return open(path, O_RDWR); +} + +static inline int flash_close(int fd) { + return close(fd); +} + +int flash_write(int partition, int data, ssize_t size, ssize_t skip); + +static inline ssize_t read_data_once(int fd, char *buffer, ssize_t size) { + ssize_t readcount = 0; + ssize_t len; + + while ((len = TEMP_FAILURE_RETRY(read(fd, (void *) &buffer[readcount], size - readcount))) > 0) { + readcount += len; + } + if (len < 0) { + D(ERR, "Read error:%s", strerror(errno)); + return len; + } + + return readcount; +} + +int flash_validate_certificate(int signed_fd, int *data_fd); + +#endif + diff --git a/fastbootd/commands/partitions.c b/fastbootd/commands/partitions.c new file mode 100644 index 0000000..74232e6 --- /dev/null +++ b/fastbootd/commands/partitions.c @@ -0,0 +1,772 @@ +/* + * Copyright (c) 2009-2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#include <endian.h> +#include <zlib.h> +#include <linux/hdreg.h> +#include <sys/ioctl.h> +#include <stdlib.h> +#include <cutils/config_utils.h> +#include <inttypes.h> + +#include "partitions.h" +#include "debug.h" +#include "utils.h" +#include "protocol.h" + +#define BLKRRPART _IO(0x12,95) /* re-read partition table */ +#define BLKSSZGET _IO(0x12,104) + +#define DIV_ROUND_UP(x, y) (((x) + (y) - 1)/(y)) +#define ALIGN(x, y) ((y) * DIV_ROUND_UP((x), (y))) +#define ALIGN_DOWN(x, y) ((y) * ((x) / (y))) + + +const uint8_t partition_type_uuid[16] = { + 0xa2, 0xa0, 0xd0, 0xeb, 0xe5, 0xb9, 0x33, 0x44, + 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7, +}; + +//TODO: There is assumption that we are using little endian + +static void GPT_entry_clear(struct GPT_entry_raw *entry) +{ + memset(entry, 0, sizeof(*entry)); +} + +/* + * returns mapped location to choosen area + * mapped_ptr is pointer to whole area mapped (it can be bigger then requested) + */ +int gpt_mmap(struct GPT_mapping *mapping, uint64_t location, int size, int fd) +{ + unsigned int location_diff = location & ~PAGE_MASK; + + mapping->size = ALIGN(size + location_diff, PAGE_SIZE); + + uint64_t sz = get_file_size64(fd); + if (sz < size + location) { + D(ERR, "the location of mapping area is outside of the device size %" PRId64, sz); + return 1; + } + location = ALIGN_DOWN(location, PAGE_SIZE); + + mapping->map_ptr = mmap64(NULL, mapping->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, location); + + if (mapping->map_ptr == MAP_FAILED) { + mapping->ptr = MAP_FAILED; + D(ERR, "map failed: %s", strerror(errno)); + return 1; + } + + mapping->ptr = (void *)((char *) mapping->map_ptr + location_diff); + return 0; +} + +void gpt_unmap(struct GPT_mapping *mapping) { + munmap(mapping->map_ptr, mapping->size); +} + + +#define LBA_ADDR(table, value) ((uint64_t) (table)->sector_size * (value)) + +int GPT_map_from_content(struct GPT_entry_table *table, const struct GPT_content *content) +{ + + // Mapping header + if (gpt_mmap(&table->header_map, LBA_ADDR(table, content->header.current_lba), + table->sector_size, table->fd)) { + D(ERR, "unable to map header:%s\n", strerror(errno)); + goto error_header; + } + + table->header = (struct GPT_header *) table->header_map.ptr; + + table->partition_table_size = ROUND_UP(content->header.entries_count * sizeof(*table->entries), + table->sector_size); + + // Mapping entry table + if (gpt_mmap(&table->entries_map, LBA_ADDR(table, content->header.entries_lba), + table->partition_table_size, table->fd)) { + D(ERR, "unable to map entries"); + goto error_signature; + } + + table->entries = (struct GPT_entry_raw *) table->entries_map.ptr; + + // Mapping secondary header + if (gpt_mmap(&table->sec_header_map, LBA_ADDR(table, content->header.backup_lba), + table->sector_size, table->fd)) { + D(ERR, "unable to map backup gpt header"); + goto error_sec_header; + } + + // Mapping secondary entries table + if (gpt_mmap(&table->sec_entries_map, + LBA_ADDR(table, content->header.backup_lba) - table->partition_table_size, + table->partition_table_size, table->fd)) { + D(ERR, "unable to map secondary gpt table"); + goto error_sec_entries; + } + + table->second_header = (struct GPT_header *) table->sec_header_map.ptr; + table->second_entries = (struct GPT_entry_raw *) table->sec_entries_map.ptr; + table->second_valid = strcmp("EFI PART", (char *) table->second_header->signature) == 0; + + return 0; + +error_sec_entries: + gpt_unmap(&table->sec_header_map); +error_sec_header: + gpt_unmap(&table->entries_map); +error_signature: + gpt_unmap(&table->header_map); +error_header: + return 1; +} + +int GPT_map(struct GPT_entry_table *table, unsigned header_lba) +{ + struct GPT_content content; + struct GPT_mapping mapping; + struct GPT_header *header; + + if (gpt_mmap(&mapping, LBA_ADDR(table, header_lba), table->sector_size, table->fd)) { + D(ERR, "unable to map header: %s", strerror(errno)); + goto error_header; + } + + header = (struct GPT_header *) mapping.ptr; + + if (strcmp("EFI PART", (char *) header->signature)) { + D(ERR, "GPT entry not valid"); + goto error_signature; + } + + content.header = *header; + + gpt_unmap(&mapping); + + return GPT_map_from_content(table, &content); + +error_signature: + gpt_unmap(&table->header_map); +error_header: + return 1; +} + +struct GPT_entry_table* GPT_get_device(const char *path, unsigned header_lba) +{ + struct GPT_entry_table *table; + size_t sector_bytes; + + table = (struct GPT_entry_table *) malloc(sizeof(*table)); + table->fd = open(path, O_RDWR); + + if (table->fd < 0) { + D(ERR, "unable to open file %s:%s\n", path, strerror(errno)); + return NULL; + } + + if (!ioctl(table->fd, BLKSSZGET, §or_bytes)) { + table->sector_size = (unsigned) sector_bytes; + D(INFO, "Got sector size %d", table->sector_size); + } else { + D(WARN, "unable to get sector size, assuming 512"); + table->sector_size = 512; + } + + if (GPT_map(table, header_lba)) { + D(ERR, "Could not map gpt"); + return NULL; + } + + return table; +} + +static struct GPT_entry_table* GPT_get_from_content(const char *path, const struct GPT_content *content) +{ + struct GPT_entry_table *table; + size_t sector_bytes; + + table = (struct GPT_entry_table *) malloc(sizeof(*table)); + table->fd = open(path, O_RDWR); + + if (table->fd < 0) { + D(ERR, "unable to open file %s:%s\n", path, strerror(errno)); + return NULL; + } + + if (!ioctl(table->fd, BLKSSZGET, §or_bytes)) { + table->sector_size = (unsigned) sector_bytes; + D(INFO, "Got sector size %d", table->sector_size); + } else { + D(WARN, "unable to get sector size %s, assuming 512", strerror(errno)); + table->sector_size = 512; + } + + if (GPT_map_from_content(table, content)) { + D(ERR, "Could not map gpt"); + return NULL; + } + + return table; +} + + +void GPT_release_device(struct GPT_entry_table *table) +{ + gpt_unmap(&table->header_map); + gpt_unmap(&table->entries_map); + gpt_unmap(&table->sec_header_map); + gpt_unmap(&table->sec_entries_map); + close(table->fd); + free(table); +} + +static int GPT_check_overlap(struct GPT_entry_table *table, struct GPT_entry_raw *entry); +static int GPT_check_overlap_except(struct GPT_entry_table *table, + struct GPT_entry_raw *entry, + struct GPT_entry_raw *exclude); + +void GPT_edit_entry(struct GPT_entry_table *table, + struct GPT_entry_raw *old_entry, + struct GPT_entry_raw *new_entry) +{ + struct GPT_entry_raw *current_entry = GPT_get_pointer(table, old_entry); + + if (GPT_check_overlap_except(table, new_entry, current_entry)) { + D(ERR, "Couldn't add overlaping partition"); + return; + } + + if (current_entry == NULL) { + D(ERR, "Couldn't find entry"); + return; + } + + *current_entry = *new_entry; +} + +int GPT_delete_entry(struct GPT_entry_table *table, struct GPT_entry_raw *entry) +{ + struct GPT_entry_raw *raw = GPT_get_pointer(table, entry); + + if (raw == NULL) { + D(ERR, "could not find entry"); + return 1; + } + D(DEBUG, "Deleting gpt entry '%s'\n", raw->partition_guid); + + // Entry in the middle of table may become empty + GPT_entry_clear(raw); + + return 0; +} + +void GPT_add_entry(struct GPT_entry_table *table, struct GPT_entry_raw *entry) +{ + unsigned i; + int inserted = 0; + if (GPT_check_overlap(table, entry)) { + D(ERR, "Couldn't add overlaping partition"); + return; + } + + if (GPT_get_pointer(table, entry) != NULL) { + D(WARN, "Add entry fault, this entry already exists"); + return; + } + + struct GPT_entry_raw *entries = table->entries; + + for (i = 0; i < table->header->entries_count; ++i) { + if (!entries[i].type_guid[0]) { + inserted = 1; + D(DEBUG, "inserting"); + memcpy(&entries[i], entry, sizeof(entries[i])); + break; + } + } + + if (!inserted) { + D(ERR, "Unable to find empty partion entry"); + } +} + +struct GPT_entry_raw *GPT_get_pointer_by_UTFname(struct GPT_entry_table *table, const uint16_t *name); + +struct GPT_entry_raw *GPT_get_pointer(struct GPT_entry_table *table, struct GPT_entry_raw *entry) +{ + if (entry->partition_guid[0] != 0) + return GPT_get_pointer_by_guid(table, (const char *) entry->partition_guid); + else if (entry->name[0] != 0) + return GPT_get_pointer_by_UTFname(table, entry->name); + + D(WARN, "Name or guid needed to find entry"); + return NULL; +} + +struct GPT_entry_raw *GPT_get_pointer_by_guid(struct GPT_entry_table *table, const char *name) +{ + int current = (int) table->header->entries_count; + + for (current = current - 1; current >= 0; --current) { + if (strncmp((char *) name, + (char *) table->entries[current].partition_guid, 16) == 0) { + return &table->entries[current]; + } + } + + return NULL; +} + +int strncmp_UTF16_char(const uint16_t *s1, const char *s2, size_t n) +{ + if (n == 0) + return (0); + do { + if (((*s1) & 127) != *s2++) + return (((unsigned char) ((*s1) & 127)) - *(unsigned char *)--s2); + if (*s1++ == 0) + break; + } while (--n != 0); + return (0); +} + +int strncmp_UTF16(const uint16_t *s1, const uint16_t *s2, size_t n) +{ + if (n == 0) + return (0); + do { + if ((*s1) != *s2++) + return (*s1 - *--s2); + if (*s1++ == 0) + break; + } while (--n != 0); + return (0); +} + +struct GPT_entry_raw *GPT_get_pointer_by_name(struct GPT_entry_table *table, const char *name) +{ + int count = (int) table->header->entries_count; + int current; + + for (current = 0; current < count; ++current) { + if (strncmp_UTF16_char(table->entries[current].name, + (char *) name, 16) == 0) { + return &table->entries[current]; + } + } + + return NULL; +} + +struct GPT_entry_raw *GPT_get_pointer_by_UTFname(struct GPT_entry_table *table, const uint16_t *name) +{ + int count = (int) table->header->entries_count; + int current; + + for (current = 0; current < count; ++current) { + if (strncmp_UTF16(table->entries[current].name, + name, GPT_NAMELEN) == 0) { + return &table->entries[current]; + } + } + + return NULL; +} + +void GPT_sync(struct GPT_entry_table *table) +{ + uint32_t crc; + + //calculate crc32 + crc = crc32(0, Z_NULL, 0); + crc = crc32(crc, (void*) table->entries, table->header->entries_count * sizeof(*table->entries)); + table->header->partition_array_checksum = crc; + + table->header->header_checksum = 0; + crc = crc32(0, Z_NULL, 0); + crc = crc32(crc, (void*) table->header, table->header->header_size); + table->header->header_checksum = crc; + + //sync secondary partion + if (table->second_valid) { + memcpy((void *)table->second_entries, (void *) table->entries, table->partition_table_size); + memcpy((void *)table->second_header, (void *)table->header, sizeof(*table->header)); + } + + if(!ioctl(table->fd, BLKRRPART, NULL)) { + D(WARN, "Unable to force kernel to refresh partition table"); + } +} + +void GPT_to_UTF16(uint16_t *to, const char *from, int n) +{ + int i; + for (i = 0; i < (n - 1) && (to[i] = from[i]) != '\0'; ++i); + to[i] = '\0'; +} + +void GPT_from_UTF16(char *to, const uint16_t *from, int n) +{ + int i; + for (i = 0; i < (n - 1) && (to[i] = from[i] & 127) != '\0'; ++i); + to[i] = '\0'; +} + +static int GPT_check_overlap_except(struct GPT_entry_table *table, + struct GPT_entry_raw *entry, + struct GPT_entry_raw *exclude) { + int current = (int) table->header->entries_count; + int dontcheck; + struct GPT_entry_raw *current_entry; + if (entry->last_lba < entry->first_lba) { + D(WARN, "Start address have to be less than end address"); + return 1; + } + + for (current = current - 1; current >= 0; --current) { + current_entry = &table->entries[current]; + dontcheck = strncmp((char *) entry->partition_guid, + (char *) current_entry->partition_guid , 16) == 0; + dontcheck |= current_entry->type_guid[0] == 0; + dontcheck |= current_entry == exclude; + + if (!dontcheck && ((entry->last_lba >= current_entry->first_lba && + entry->first_lba < current_entry->last_lba ))) { + return 1; + } + } + + return 0; +} + +static int GPT_check_overlap(struct GPT_entry_table *table, struct GPT_entry_raw *entry) +{ + return GPT_check_overlap_except(table, entry, NULL); +} + +static char *get_key_value(char *ptr, char **key, char **value) +{ + *key = ptr; + ptr = strchr(ptr, '='); + + if (ptr == NULL) + return NULL; + + *ptr++ = '\0'; + *value = ptr; + ptr = strchr(ptr, ';'); + + if (ptr == NULL) + ptr = *value + strlen(*value); + else + *ptr = '\0'; + + *key = strip(*key); + *value = strip(*value); + + return ptr; +} + +//TODO: little endian? +static int add_key_value(const char *key, const char *value, struct GPT_entry_raw *entry) +{ + char *endptr; + if (!strcmp(key, "type")) { + strncpy((char *) entry->type_guid, value, 16); + entry->type_guid[15] = 0; + } + else if (!strcmp(key, "guid")) { + strncpy((char *) entry->partition_guid, value, 16); + entry->type_guid[15] = 0; + } + else if (!strcmp(key, "firstlba")) { + entry->first_lba = strtoul(value, &endptr, 10); + if (*endptr != '\0') goto error; + } + else if (!strcmp(key, "lastlba")) { + entry->last_lba = strtoul(value, &endptr, 10); + if (*endptr != '\0') goto error; + } + else if (!strcmp(key, "flags")) { + entry->flags = strtoul(value, &endptr, 16); + if (*endptr != '\0') goto error; + } + else if (!strcmp(key, "name")) { + GPT_to_UTF16(entry->name, value, GPT_NAMELEN); + } + else { + goto error; + } + + return 0; + +error: + D(ERR, "Could not find key or parse value: %s,%s", key, value); + return 1; +} + +int GPT_parse_entry(char *string, struct GPT_entry_raw *entry) +{ + char *ptr = string; + char *key, *value; + + while ((ptr = get_key_value(ptr, &key, &value)) != NULL) { + if (add_key_value(key, value, entry)) { + D(WARN, "key or value not valid: %s %s", key, value); + return 1; + } + } + + return 0; +} + +void entry_set_guid(int n, uint8_t *guid) +{ + int fd; + fd = open("/dev/urandom", O_RDONLY); + read(fd, guid, 16); + close(fd); + + //rfc4122 + guid[8] = (guid[8] & 0x3F) | 0x80; + guid[7] = (guid[7] & 0x0F) | 0x40; +} + +void GPT_default_content(struct GPT_content *content, struct GPT_entry_table *table) +{ + if (table != NULL) { + memcpy(&content->header, table->header, sizeof(content->header)); + content->header.header_size = sizeof(content->header); + content->header.entry_size = sizeof(struct GPT_entry_raw); + } + else { + D(WARN, "Could not locate old gpt table, using default values"); + memset(&content->header, 0, sizeof(content->header) / sizeof(int)); + content->header = (struct GPT_header) { + .revision = 0x10000, + .header_size = sizeof(content->header), + .header_checksum = 0, + .reserved_zeros = 0, + .current_lba = 1, + .backup_lba = 1, + .entry_size = sizeof(struct GPT_entry_raw), + .partition_array_checksum = 0 + }; + strncpy((char *)content->header.signature, "EFI PART", 8); + strncpy((char *)content->header.disk_guid, "ANDROID MMC DISK", 16); + } +} + +static int get_config_uint64(cnode *node, uint64_t *ptr, const char *name) +{ + const char *tmp; + uint64_t val; + char *endptr; + if ((tmp = config_str(node, name, NULL))) { + val = strtoull(tmp, &endptr, 10); + if (*endptr != '\0') { + D(WARN, "Value for %s is not a number: %s", name, tmp); + return 1; + } + *ptr = val; + return 0; + } + return 1; +} + +static int get_config_string(cnode *node, char *ptr, int max_len, const char *name) +{ + size_t begin, end; + const char *value = config_str(node, name, NULL); + if (!value) + return -1; + + begin = strcspn(value, "\"") + 1; + end = strcspn(&value[begin], "\""); + + if ((int) end > max_len) { + D(WARN, "Identifier \"%s\" too long", value); + return -1; + } + + strncpy(ptr, &value[begin], end); + if((int) end < max_len) + ptr[end] = 0; + return 0; +} + +static void GPT_parse_header(cnode *node, struct GPT_content *content) +{ + get_config_uint64(node, &content->header.current_lba, "header_lba"); + get_config_uint64(node, &content->header.backup_lba, "backup_lba"); + get_config_uint64(node, &content->header.first_usable_lba, "first_lba"); + get_config_uint64(node, &content->header.last_usable_lba, "last_lba"); + get_config_uint64(node, &content->header.entries_lba, "entries_lba"); + get_config_string(node, (char *) content->header.disk_guid, 16, "guid"); +} + +static int GPT_parse_partitions(cnode *node, struct GPT_content *content) +{ + cnode *current; + int i; + uint64_t partition_size; + struct GPT_entry_raw *entry; + for (i = 0, current = node->first_child; current; current = current->next, ++i) { + entry = &content->entries[i]; + entry_set_guid(i, content->entries[i].partition_guid); + memcpy(&content->entries[i].type_guid, partition_type_uuid, 16); + if (get_config_uint64(current, &entry->first_lba, "first_lba")) { + D(ERR, "first_lba not specified"); + return 1; + } + if (get_config_uint64(current, &partition_size, "partition_size")) { + D(ERR, "partition_size not specified"); + return 1; + } + if (config_str(current, "system", NULL)) { + entry->flags |= GPT_FLAG_SYSTEM; + } + if (config_str(current, "bootable", NULL)) { + entry->flags |= GPT_FLAG_BOOTABLE; + } + if (config_str(current, "readonly", NULL)) { + entry->flags |= GPT_FLAG_READONLY; + } + if (config_str(current, "automount", NULL)) { + entry->flags |= GPT_FLAG_DOAUTOMOUNT; + } + + get_config_uint64(current, &content->entries[i].flags, "flags"); + content->entries[i].last_lba = content->entries[i].first_lba + partition_size - 1; + GPT_to_UTF16(content->entries[i].name, current->name, 16); + } + return 0; +} + +static inline int cnode_count(cnode *node) +{ + int i; + cnode *current; + for (i = 0, current = node->first_child; current; current = current->next, ++i) + ; + return i; +} + + +static int GPT_parse_cnode(cnode *root, struct GPT_content *content) +{ + cnode *partnode; + + if (!(partnode = config_find(root, "partitions"))) { + D(ERR, "Could not find partition table"); + return 0; + } + + GPT_parse_header(root, content); + + content->header.entries_count = cnode_count(partnode); + content->entries = malloc(content->header.entries_count * sizeof(struct GPT_entry_raw)); + + if (GPT_parse_partitions(partnode, content)) { + D(ERR, "Could not parse partitions"); + return 0; + } + + return 1; +} + +int GPT_parse_file(int fd, struct GPT_content *content) +{ + char *data; + int size; + int ret; + cnode *root = config_node("", ""); + + size = get_file_size(fd); + data = (char *) mmap(NULL, size + 1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + + if (data == NULL) { + if (size == 0) + D(ERR, "config file empty"); + else + D(ERR, "Out of memory"); + return 0; + } + + data[size - 1] = 0; + config_load(root, data); + + if (root->first_child == NULL) { + D(ERR, "Could not read config file"); + return 0; + } + + ret = GPT_parse_cnode(root, content); + munmap(data, size); + return ret; +} + +void GPT_release_content(struct GPT_content *content) +{ + free(content->entries); +} + +int GPT_write_content(const char *device, struct GPT_content *content) +{ + struct GPT_entry_table *maptable; + + maptable = GPT_get_from_content(device, content); + if (maptable == NULL) { + D(ERR, "could not map device"); + return 0; + } + + memcpy(maptable->header, &content->header, sizeof(*maptable->header)); + memcpy(maptable->entries, content->entries, + content->header.entries_count * sizeof(*maptable->entries)); + + GPT_sync(maptable); + GPT_release_device(maptable); + + return 1; +} + diff --git a/fastbootd/commands/partitions.h b/fastbootd/commands/partitions.h new file mode 100644 index 0000000..9a6a88d --- /dev/null +++ b/fastbootd/commands/partitions.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2009-2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#ifndef __FASTBOOTD_PATITIONS_ +#define __FASTBOOTD_PATITIONS_ + +#include <stdint.h> + +#define GPT_ENTRIES 128 +#define GPT_NAMELEN 36 + +#define GPT_FLAG_SYSTEM (1ULL << 0) +#define GPT_FLAG_BOOTABLE (1ULL << 2) +#define GPT_FLAG_READONLY (1ULL << 60) +#define GPT_FLAG_DOAUTOMOUNT (1ULL << 63) + +// it should be passed in little endian order +struct GPT_entry_raw { + uint8_t type_guid[16]; + uint8_t partition_guid[16]; + uint64_t first_lba; // little endian + uint64_t last_lba; + uint64_t flags; + uint16_t name[GPT_NAMELEN]; // UTF-16 LE +}; + +struct GPT_mapping { + void *map_ptr; + void *ptr; + unsigned size; +}; + +struct GPT_entry_table { + int fd; + + struct GPT_mapping header_map; + struct GPT_mapping entries_map; + struct GPT_mapping sec_header_map; + struct GPT_mapping sec_entries_map; + + struct GPT_header *header; + struct GPT_entry_raw *entries; + struct GPT_header *second_header; + struct GPT_entry_raw *second_entries; + + unsigned sector_size; + unsigned partition_table_size; + int second_valid; +}; + +struct GPT_header { + uint8_t signature[8]; + uint32_t revision; + uint32_t header_size; + uint32_t header_checksum; + uint32_t reserved_zeros; + uint64_t current_lba; + uint64_t backup_lba; + uint64_t first_usable_lba; + uint64_t last_usable_lba; + uint8_t disk_guid[16]; + uint64_t entries_lba; + uint32_t entries_count; + uint32_t entry_size; + uint32_t partition_array_checksum; + // the rest should be filled with zeros +} __attribute__((packed)); + +struct GPT_content { + struct GPT_header header; + struct GPT_entry_raw *entries; +}; + + +struct GPT_entry_table* GPT_get_device(const char *, unsigned lba); + +void GPT_release_device(struct GPT_entry_table *); + +void GPT_edit_entry(struct GPT_entry_table *table, + struct GPT_entry_raw *old_entry, + struct GPT_entry_raw *new_entry); + +int GPT_delete_entry(struct GPT_entry_table *table, struct GPT_entry_raw *entry); + +void GPT_add_entry(struct GPT_entry_table *table, struct GPT_entry_raw *entry); + +struct GPT_entry_raw *GPT_get_pointer(struct GPT_entry_table *table, struct GPT_entry_raw *entry); +struct GPT_entry_raw *GPT_get_pointer_by_guid(struct GPT_entry_table *, const char *); +struct GPT_entry_raw *GPT_get_pointer_by_name(struct GPT_entry_table *, const char *); + +//Use after every edit operation +void GPT_sync(); + +void GPT_to_UTF16(uint16_t *, const char *, int ); +void GPT_from_UTF16(char *, const uint16_t *, int); + +int GPT_parse_entry(char *string, struct GPT_entry_raw *entry); + +void GPT_default_content(struct GPT_content *content, struct GPT_entry_table *table); + +void GPT_release_content(struct GPT_content *content); + +int GPT_parse_file(int fd, struct GPT_content *content); + +int GPT_write_content(const char *device, struct GPT_content *content); + +int gpt_mmap(struct GPT_mapping *mapping, uint64_t location, int size, int fd); + +void gpt_unmap(struct GPT_mapping *mapping); + +#endif diff --git a/fastbootd/commands/virtual_partitions.c b/fastbootd/commands/virtual_partitions.c new file mode 100644 index 0000000..813f485 --- /dev/null +++ b/fastbootd/commands/virtual_partitions.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2009-2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "commands/virtual_partitions.h" +#include "debug.h" + +static struct virtual_partition *partitions = NULL; + +int try_handle_virtual_partition(struct protocol_handle *handle, const char *arg) +{ + struct virtual_partition *current; + + for (current = partitions; current != NULL; current = current->next) { + if (!strcmp(current->name, arg)) { + current->handler(handle, arg); + } + } + + return 0; +} + +void virtual_partition_register( + const char * name, + void (*handler)(struct protocol_handle *phandle, const char *arg)) +{ + struct virtual_partition *new; + new = malloc(sizeof(*new)); + if (new) { + new->name = name; + new->handler = handler; + new->next = partitions; + partitions = new; + } + else { + D(ERR, "Out of memory"); + } +} diff --git a/fastbootd/commands/virtual_partitions.h b/fastbootd/commands/virtual_partitions.h new file mode 100644 index 0000000..88df71e --- /dev/null +++ b/fastbootd/commands/virtual_partitions.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2009-2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef FASTBOOTD_VIRTUAL_PARTITIONS_H +#define FASTBOOTD_VIRTUAL_PARTITIONS_H + +#include "protocol.h" + +struct virtual_partition { + struct virtual_partition *next; + const char *name; + void (*handler)(struct protocol_handle *phandle, const char *arg); +}; + +int try_handle_virtual_partition(struct protocol_handle *handle, const char *arg); + +void virtual_partition_register( + const char * name, + void (*handler)(struct protocol_handle *phandle, const char *arg)); + +#endif diff --git a/fastbootd/config.c b/fastbootd/config.c index b8503fd..fe6da69 100644 --- a/fastbootd/config.c +++ b/fastbootd/config.c @@ -35,29 +35,13 @@ #include <sys/types.h> #include "protocol.h" +#include "utils.h" #include "debug.h" // TODO: change config path #define CONFIG_PATH "/data/fastboot.cfg" -static char *strip(char *str) -{ - int n; - - n = strspn(str, " \t"); - str += n; - - for (n = strlen(str) - 1; n >= 0; n--) { - if (str[n] == ' ' || str[n] == '\t') - str[n] = '\0'; - else - break; - } - - return str; -} - static int config_parse_line(char *line) { char *c; diff --git a/fastbootd/fastbootd.c b/fastbootd/fastbootd.c index 98df0db..2b51b33 100644 --- a/fastbootd/fastbootd.c +++ b/fastbootd/fastbootd.c @@ -16,30 +16,85 @@ #include <stdio.h> #include <unistd.h> - #include <cutils/klog.h> +#include <getopt.h> +#include <stdlib.h> #include "debug.h" +#include "trigger.h" +#include "socket_client.h" +#include "secure.h" unsigned int debug_level = DEBUG; void commands_init(); void usb_init(); void config_init(); +int transport_socket_init(); +int network_discovery_init(); +void ssh_server_start(); int main(int argc, char **argv) { + int socket_client = 0; + int c; + int network = 1; + + klog_init(); + klog_set_level(6); + + const struct option longopts[] = { + {"socket", no_argument, 0, 'S'}, + {"nonetwork", no_argument, 0, 'n'}, + {0, 0, 0, 0} + }; + + while (1) { + c = getopt_long(argc, argv, "Sn", longopts, NULL); + /* Alphabetical cases */ + if (c < 0) + break; + switch (c) { + case 'S': + socket_client = 1; + break; + case 'n': + network = 0; + break; + case '?': + return 1; + default: + return 0; + } + } + (void)argc; (void)argv; klog_init(); klog_set_level(6); - config_init(); - commands_init(); - usb_init(); - while (1) { - sleep(1); + if (socket_client) { + //TODO: Shouldn't we change current tty into raw mode? + run_socket_client(); + } + else { + cert_init_crypto(); + config_init(); + load_trigger(); + commands_init(); + usb_init(); + + if (network) { + if (!transport_socket_init()) + exit(1); + ssh_server_start(); + network_discovery_init(); + } + + while (1) { + sleep(1); + } } return 0; } diff --git a/fastbootd/include/vendor_trigger.h b/fastbootd/include/vendor_trigger.h new file mode 100644 index 0000000..51204fa --- /dev/null +++ b/fastbootd/include/vendor_trigger.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2009-2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef __VENDOR_TRIGGER_H_ +#define __VENDOR_TRIGGER_H_ + +#define TRIGGER_MODULE_ID "fastbootd" +#include <hardware/hardware.h> + +__BEGIN_DECLS + +struct GPT_entry_raw; +struct GPT_content; + +/* + * Structer with function pointers may become longer in the future + */ + +struct vendor_trigger_t { + struct hw_device_t common; + + /* + * This function runs at the beggining and shoud never be changed + * + * version is number parameter indicating version on the fastbootd side + * libversion is version indicateing version of the library version + * + * returns 0 if it can cooperate with the current version and 1 in opposite + */ + int (*check_version)(const int version, int *libversion); + + + /* + * Return value -1 forbid the action from the vendor site and sets errno + */ + int (* gpt_layout)(struct GPT_content *); + int (* oem_cmd)(const char *arg, const char **response); +}; + +__END_DECLS + +#endif diff --git a/fastbootd/network_discovery.c b/fastbootd/network_discovery.c new file mode 100644 index 0000000..1cd3e48 --- /dev/null +++ b/fastbootd/network_discovery.c @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2009-2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <string.h> +#include <dns_sd.h> +#include <cutils/properties.h> +#include <unistd.h> + +#include "debug.h" +#include "network_discovery.h" +#include "utils.h" + +#define MDNS_SERVICE_NAME "mdnsd" +#define MDNS_SERVICE_STATUS "init.svc.mdnsd" +#define FASTBOOTD_TYPE "_fastbootd._tcp" +#define FASTBOOTD_DOMAIN "local." +#define FASTBOOTD_NAME "fastbootd" + + +static void reg_reply(DNSServiceRef sdref, const DNSServiceFlags flags, DNSServiceErrorType errorCode, + const char *name, const char *regtype, const char *domain, void *context) +{ + (void)sdref; // Unused + (void)flags; // Unused + (void)context; // Unused + if (errorCode == kDNSServiceErr_ServiceNotRunning) { + fprintf(stderr, "Error code %d\n", errorCode); + } + + + printf("Got a reply for service %s.%s%s: ", name, regtype, domain); + + if (errorCode == kDNSServiceErr_NoError) + { + if (flags & kDNSServiceFlagsAdd) + printf("Name now registered and active\n"); + else + printf("Name registration removed\n"); + if (errorCode == kDNSServiceErr_NameConflict) + printf("Name in use, please choose another\n"); + else + printf("Error %d\n", errorCode); + + if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); + } +} + +static int register_service() { + DNSServiceRef sdref = NULL; + const char *domain = FASTBOOTD_DOMAIN; + const char *type = FASTBOOTD_TYPE; + const char *host = NULL; + char name[PROP_VALUE_MAX]; + uint16_t port = 22; + int flags = 0; + DNSServiceErrorType result; + property_get("ro.serialno", name, ""); + if (!strcmp(name, "")) { + D(ERR, "No property serialno"); + return -1; + } + + result = DNSServiceRegister(&sdref, flags, kDNSServiceInterfaceIndexAny, + name, type, domain, host, port, + 0, NULL, reg_reply, NULL); + if (result != kDNSServiceErr_NoError) { + D(ERR, "Unable to register service"); + return -1; + } + return 0; +} + + +int network_discovery_init() +{ + D(INFO, "Starting discovery"); + if (service_start(MDNS_SERVICE_NAME)) { + D(ERR, "Unable to start discovery"); + return -1; + } + + if (register_service()) { + D(ERR, "Unable to register service"); + return -1; + } + + return 0; +} + diff --git a/fastbootd/network_discovery.h b/fastbootd/network_discovery.h new file mode 100644 index 0000000..75fda63 --- /dev/null +++ b/fastbootd/network_discovery.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2009-2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _FASTBOOTD_NETWORK_DISCOVERY_H +#define _FASTBOOTD_NETWORK_DISCOVERY_H + +int network_discovery_init(); + +#endif diff --git a/fastbootd/other/gptedit.c b/fastbootd/other/gptedit.c new file mode 100644 index 0000000..d423529 --- /dev/null +++ b/fastbootd/other/gptedit.c @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2009-2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <getopt.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <cutils/klog.h> + +#include "commands/partitions.h" +#include "debug.h" + +unsigned int debug_level = DEBUG; +//TODO: add tool to generate config file + +void usage() { + fprintf(stderr, + "usage: test_gpt [ <option> ] <file>\n" + "\n" + "options:\n" + " -p print partitions\n" + " -c print config file\n" + " -a adds new partition\n" + " -d deletes partition (-o needed)\n" + "\n" + " -n name@startlba,endlba new partition detail\n" + " -o old partition name\n" + " -t type guid\n" + " -g partition guid\n" + " -l gpt_location specyfies gpt secto\n" + ); + +} + +void printGPT(struct GPT_entry_table *table); +void addGPT(struct GPT_entry_table *table, const char *arg, const char *guid, const char *tguid); +void deleteGPT(struct GPT_entry_table *table, const char *name); +void configPrintGPT(struct GPT_entry_table *table); + +int main(int argc, char *argv[]) { + int print_cmd = 0; + int config_cmd = 0; + int add_cmd = 0; + int del_cmd = 0; + int sync_cmd = 0; + int c; + const char *new_partition = NULL; + const char *old_partition = NULL; + const char *type_guid = NULL; + const char *partition_guid = NULL; + unsigned gpt_location = 1; + + klog_init(); + klog_set_level(6); + + const struct option longopts[] = { + {"print", no_argument, 0, 'p'}, + {"config-print", no_argument, 0, 'c'}, + {"add", no_argument, 0, 'a'}, + {"del", no_argument, 0, 'd'}, + {"new", required_argument, 0, 'n'}, + {"old", required_argument, 0, 'o'}, + {"type", required_argument, 0, 't'}, + {"sync", required_argument, 0, 's'}, + {"guid", required_argument, 0, 'g'}, + {"location", required_argument, 0, 'l'}, + {0, 0, 0, 0} + }; + + while (1) { + c = getopt_long(argc, argv, "pcadt:g:n:o:sl:", longopts, NULL); + /* Alphabetical cases */ + if (c < 0) + break; + switch (c) { + case 'p': + print_cmd = 1; + break; + case 'c': + config_cmd = 1; + break; + case 'a': + add_cmd = 1; + break; + case 'd': + del_cmd = 1; + break; + case 'n': + new_partition = optarg; + break; + case 'o': + old_partition = optarg; + break; + case 't': + type_guid = optarg; + case 'g': + partition_guid = optarg; + break; + case 's': + sync_cmd = 1; + break; + case 'l': + gpt_location = strtoul(optarg, NULL, 10); + fprintf(stderr, "Got offset as %d", gpt_location); + break; + case '?': + return 1; + default: + abort(); + } + } + + argc -= optind; + argv += optind; + + if (argc < 1) { + usage(); + return 1; + } + + const char *path = argv[0]; + struct GPT_entry_table *table = GPT_get_device(path, gpt_location); + if (table == NULL) { + fprintf(stderr, "unable to get GPT table from %s\n", path); + return 1; + } + + if (add_cmd) + addGPT(table, new_partition, partition_guid, type_guid); + if (del_cmd) + deleteGPT(table, old_partition); + if (print_cmd) + printGPT(table); + if (config_cmd) + configPrintGPT(table); + if (sync_cmd) + GPT_sync(table); + + GPT_release_device(table); + + return 0; +} + +void printGPT(struct GPT_entry_table *table) { + struct GPT_entry_raw *entry = table->entries; + unsigned n, m; + char name[GPT_NAMELEN + 1]; + + printf("ptn start block end block name\n"); + printf("---- ------------- -------------\n"); + + for (n = 0; n < table->header->entries_count; n++, entry++) { + if (entry->type_guid[0] == 0) + continue; + for (m = 0; m < GPT_NAMELEN; m++) { + name[m] = entry->name[m] & 127; + } + name[m] = 0; + printf("#%03d %13"PRId64" %13"PRId64" %s\n", + n + 1, entry->first_lba, entry->last_lba, name); + } +} + +void configPrintGPT(struct GPT_entry_table *table) { + struct GPT_entry_raw *entry = table->entries; + unsigned n, m; + char name[GPT_NAMELEN + 1]; + char temp_guid[17]; + temp_guid[16] = 0; + + printf("header_lba %"PRId64"\n", table->header->current_lba); + printf("backup_lba %"PRId64"\n", table->header->backup_lba); + printf("first_lba %"PRId64"\n", table->header->first_usable_lba); + printf("last_lba %"PRId64"\n", table->header->last_usable_lba); + printf("entries_lba %"PRId64"\n", table->header->entries_lba); + snprintf(temp_guid, 17, "%s", table->header->disk_guid); + printf("guid \"%s\"", temp_guid); + + printf("\npartitions {\n"); + + for (n = 0; n < table->header->entries_count; n++, entry++) { + uint64_t size = entry->last_lba - entry->first_lba + 1; + + if (entry->type_guid[0] == 0) + continue; + for (m = 0; m < GPT_NAMELEN; m++) { + name[m] = entry->name[m] & 127; + } + name[m] = 0; + + printf(" %s {\n", name); + snprintf(temp_guid, 17, "%s", entry->partition_guid); + printf(" guid \"%s\"\n", temp_guid); + printf(" first_lba %"PRId64"\n", entry->first_lba); + printf(" partition_size %"PRId64"\n", size); + if (entry->flags & GPT_FLAG_SYSTEM) + printf(" system\n"); + if (entry->flags & GPT_FLAG_BOOTABLE) + printf(" bootable\n"); + if (entry->flags & GPT_FLAG_READONLY) + printf(" readonly\n"); + if (entry->flags & GPT_FLAG_DOAUTOMOUNT) + printf(" automount\n"); + printf(" }\n\n"); + } + printf("}\n"); +} + +void addGPT(struct GPT_entry_table *table, const char *str , const char *guid, const char *tguid) { + char *c, *c2; + char *arg = malloc(strlen(str)); + char *name = arg; + unsigned start, end; + strcpy(arg, str); + if (guid == NULL || tguid == NULL) { + fprintf(stderr, "Type guid and partion guid needed"); + free(arg); + return; + } + + c = strchr(arg, '@'); + + if (c == NULL) { + fprintf(stderr, "Wrong entry format"); + free(arg); + return; + } + + *c++ = '\0'; + + c2 = strchr(c, ','); + + if (c2 == NULL) { + fprintf(stderr, "Wrong entry format"); + free(arg); + return; + } + + start = strtoul(c, NULL, 10); + *c2++ = '\0'; + end = strtoul(c2, NULL, 10); + + struct GPT_entry_raw data; + strncpy((char *)data.partition_guid, guid, 15); + data.partition_guid[15] = '\0'; + strncpy((char *)data.type_guid, tguid, 15); + data.type_guid[15] = '\0'; + GPT_to_UTF16(data.name, name, GPT_NAMELEN); + data.first_lba = start; + data.last_lba = end; + + fprintf(stderr, "Adding (%d,%d) %s as, [%s, %s]", start, end, name, (char *) data.type_guid, (char *) data.partition_guid); + GPT_add_entry(table, &data); + free(arg); +} + +void deleteGPT(struct GPT_entry_table *table, const char *name) { + struct GPT_entry_raw *entry; + + if (name == NULL) { + fprintf(stderr, "Need partition name"); + return; + } + + entry = GPT_get_pointer_by_name(table, name); + + if (!entry) { + fprintf(stderr, "Unable to find partition: %s", name); + return; + } + GPT_delete_entry(table, entry); +} + diff --git a/fastbootd/other/partitions.sample.cfg b/fastbootd/other/partitions.sample.cfg new file mode 100644 index 0000000..49562cf --- /dev/null +++ b/fastbootd/other/partitions.sample.cfg @@ -0,0 +1,60 @@ + +header_lba 1 +backup_lba 101 +first_lba 43 +last_lba 100 +entries_lba 2 + +partitions { + #You can generate this as output from gptedit -c + SOS { + first_lba 28672 + partition_size 16384 + } + + DTB { + first_lba 45056 + partition_size 8192 + } + + LNX { + first_lba 53248 + partition_size 16384 + } + + APP { + first_lba 69632 + partition_size 1048576 + } + + CAC { + first_lba 1118208 + partition_size 1572864 + } + + MSC { + first_lba 2691072 + partition_size 4096 + } + + USP { + first_lba 2695168 + partition_size 65536 + } + + MDA { + first_lba 2760704 + partition_size 4096 + } + + FCT { + first_lba 2764800 + partition_size 32768 + } + + UDA { + first_lba 2797568 + partition_size 27975680 + } +} + diff --git a/fastbootd/other/sign/src/SignImg.java b/fastbootd/other/sign/src/SignImg.java new file mode 100644 index 0000000..338d427 --- /dev/null +++ b/fastbootd/other/sign/src/SignImg.java @@ -0,0 +1,181 @@ +package signtool; + +import java.io.*; +import java.util.Properties; +import java.util.ArrayList; + +import javax.mail.internet.*; +import javax.mail.MessagingException; +import javax.mail.Session; +import javax.activation.MailcapCommandMap; +import javax.activation.CommandMap; + +import java.security.PrivateKey; +import java.security.Security; +import java.security.KeyFactory; +import java.security.KeyStore; +import java.security.NoSuchAlgorithmException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.InvalidKeySpecException; +import java.security.cert.X509Certificate; +import java.security.cert.CertificateFactory; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateEncodingException; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; +import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.bouncycastle.cms.CMSProcessableByteArray; +import org.bouncycastle.cms.CMSSignedGenerator; +import org.bouncycastle.cms.CMSSignedDataGenerator; +import org.bouncycastle.cms.CMSSignedGenerator; +import org.bouncycastle.cms.CMSProcessable; +import org.bouncycastle.cms.CMSSignedData; +import org.bouncycastle.cms.CMSTypedData; +import org.bouncycastle.cert.jcajce.JcaCertStore; +import org.bouncycastle.util.Store; +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.DEROutputStream; +import org.bouncycastle.asn1.ASN1Object; + + +public class SignImg { + + /* It reads private key in pkcs#8 formate + * Conversion: + * openssl pkcs8 -topk8 -nocrypt -outform DER < inkey.pem > outkey.pk8 + */ + private static PrivateKey getPrivateKey(String path) throws IOException, FileNotFoundException, NoSuchAlgorithmException, InvalidKeySpecException { + File file = new File(path); + FileInputStream fis = new FileInputStream(file); + byte[] data = new byte[(int)file.length()]; + fis.read(data); + fis.close(); + + PKCS8EncodedKeySpec kspec = new PKCS8EncodedKeySpec(data); + KeyFactory kf = KeyFactory.getInstance("RSA"); + PrivateKey privateKey = kf.generatePrivate(kspec); + + return privateKey; + } + + private static MimeBodyPart getContent(String path) throws IOException, FileNotFoundException, MessagingException { + MimeBodyPart body = new MimeBodyPart(); + + File file = new File(path); + FileInputStream fis = new FileInputStream(file); + byte[] data = new byte[(int)file.length()]; + fis.read(data); + fis.close(); + + body.setContent(data, "application/octet-stream"); + + return body; + } + + private static CMSProcessableByteArray getCMSContent(String path) throws IOException, FileNotFoundException, MessagingException { + File file = new File(path); + FileInputStream fis = new FileInputStream(file); + byte[] data = new byte[(int)file.length()]; + fis.read(data); + fis.close(); + CMSProcessableByteArray cms = new CMSProcessableByteArray(data); + + return cms; + } + + private static X509Certificate readCert(String path) throws IOException, FileNotFoundException, CertificateException { + File file = new File(path); + FileInputStream is = new FileInputStream(file); + + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + Certificate cert = cf.generateCertificate(is); + is.close(); + + return (X509Certificate) cert; + } + + private static void save(MimeBodyPart content, String path) throws IOException, FileNotFoundException, MessagingException { + File file = new File(path); + FileOutputStream os = new FileOutputStream(file); + + content.writeTo(os); + + os.close(); + } + + private static Store certToStore(X509Certificate certificate) throws CertificateEncodingException { + ArrayList<X509Certificate> certList = new ArrayList<X509Certificate>(); + certList.add(certificate); + return new JcaCertStore(certList); + } + + public static void setDefaultMailcap() + { + MailcapCommandMap _mailcap = + (MailcapCommandMap)CommandMap.getDefaultCommandMap(); + + _mailcap.addMailcap("application/pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_signature"); + _mailcap.addMailcap("application/pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_mime"); + _mailcap.addMailcap("application/x-pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_signature"); + _mailcap.addMailcap("application/x-pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_mime"); + _mailcap.addMailcap("multipart/signed;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.multipart_signed"); + + CommandMap.setDefaultCommandMap(_mailcap); + } + + public static void main(String[] args) { + try { + if (args.length < 4) { + System.out.println("Usage: signimg data private_key certificate output"); + return; + } + System.out.println("Signing the image"); + setDefaultMailcap(); + + Security.addProvider(new BouncyCastleProvider()); + + PrivateKey key = getPrivateKey(args[1]); + System.out.println("File read sucessfully"); + + CMSSignedDataGenerator generator = new CMSSignedDataGenerator(); + + CMSTypedData body = getCMSContent(args[0]); + System.out.println("Content read sucessfully"); + + X509Certificate cert = (X509Certificate) readCert(args[2]); + System.out.println("Certificate read sucessfully"); + + ContentSigner sha256Signer = new JcaContentSignerBuilder("SHA256withRSA").setProvider("BC").build(key); + + Store certs = certToStore(cert); + + generator.addCertificates(certs); + generator.addSignerInfoGenerator( + new JcaSignerInfoGeneratorBuilder( + new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()) + .build(sha256Signer, cert)); + + CMSSignedData signed = generator.generate(body, true); + System.out.println("Signed"); + + Properties props = System.getProperties(); + Session session = Session.getDefaultInstance(props, null); + + File file = new File(args[3]); + FileOutputStream os = new FileOutputStream(file); + + ASN1InputStream asn1 = new ASN1InputStream(signed.getEncoded()); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + DEROutputStream dOut = new DEROutputStream(os); + dOut.writeObject(ASN1Object.fromByteArray(signed.getEncoded())); + + } + catch (Exception ex) { + System.out.println("Exception during programm execution: " + ex.getMessage()); + } + } +} diff --git a/fastbootd/other/vendor_trigger.c b/fastbootd/other/vendor_trigger.c new file mode 100644 index 0000000..101959b --- /dev/null +++ b/fastbootd/other/vendor_trigger.c @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2009-2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdlib.h> + +#include "vendor_trigger.h" +#include "debug.h" + +unsigned int debug_level = DEBUG; + +static const int version = 1; + +int check_version(const int fastboot_version, int *libversion) { + *libversion = version; + return !(fastboot_version == version); +} + +int gpt_layout(struct GPT_content *table) { + D(DEBUG, "message from libvendor"); + return 0; +} + +int oem_cmd(const char *arg, const char **response) { + D(DEBUG, "message from libvendor, oem catched request %s", arg); + return 0; +} + +static int close_triggers(struct vendor_trigger_t *dev) +{ + if (dev) + free(dev); + + return 0; +} + +static int open_triggers(const struct hw_module_t *module, char const *name, + struct hw_device_t **device) { + struct vendor_trigger_t *dev = malloc(sizeof(struct vendor_trigger_t)); + klog_init(); + klog_set_level(6); + + memset(dev, 0, sizeof(*dev)); + dev->common.module = (struct hw_module_t *) module; + dev->common.close = (int (*)(struct hw_device_t *)) close_triggers; + + dev->gpt_layout = gpt_layout; + dev->oem_cmd = oem_cmd; + + *device = (struct hw_device_t *) dev; + + return 0; +} + + +static struct hw_module_methods_t trigger_module_methods = { + .open = open_triggers, +}; + +struct hw_module_t HAL_MODULE_INFO_SYM = { + .tag = HARDWARE_MODULE_TAG, + .version_major = 1, + .version_minor = 0, + .id = TRIGGER_MODULE_ID, + .name = "vendor trigger library for fastbootd", + .author = "Google, Inc.", + .methods = &trigger_module_methods, +}; + diff --git a/fastbootd/secure.c b/fastbootd/secure.c new file mode 100644 index 0000000..186e026 --- /dev/null +++ b/fastbootd/secure.c @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2009-2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#include <openssl/pem.h> +#include <openssl/engine.h> +#include <openssl/x509.h> +#include <openssl/x509v3.h> +#include <openssl/pem.h> +#include <openssl/cms.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "secure.h" +#include "debug.h" +#include "utils.h" + + +void cert_init_crypto() { + CRYPTO_malloc_init(); + ERR_load_crypto_strings(); + OpenSSL_add_all_algorithms(); + ENGINE_load_builtin_engines(); +} + +X509_STORE *cert_store_from_path(const char *path) { + + X509_STORE *store; + struct stat st; + X509_LOOKUP *lookup; + + if (stat(path, &st)) { + D(ERR, "Unable to stat cert path"); + goto error; + } + + if (!(store = X509_STORE_new())) { + goto error; + } + + if (S_ISDIR(st.st_mode)) { + lookup = X509_STORE_add_lookup(store,X509_LOOKUP_hash_dir()); + if (lookup == NULL) + goto error; + if (!X509_LOOKUP_add_dir(lookup, path, X509_FILETYPE_PEM)) { + D(ERR, "Error loading cert directory %s", path); + goto error; + } + } + else if(S_ISREG(st.st_mode)) { + lookup = X509_STORE_add_lookup(store,X509_LOOKUP_file()); + if (lookup == NULL) + goto error; + if (!X509_LOOKUP_load_file(lookup, path, X509_FILETYPE_PEM)) { + D(ERR, "Error loading cert directory %s", path); + goto error; + } + } + else { + D(ERR, "cert path is not directory or regular file"); + goto error; + } + + return store; + +error: + return NULL; +} + + +int cert_read(int fd, CMS_ContentInfo **content, BIO **output) { + BIO *input; + *output = NULL; + + + input = BIO_new_fd(fd, BIO_NOCLOSE); + if (input == NULL) { + D(ERR, "Unable to open input"); + goto error; + } + + //TODO: + // read with d2i_CMS_bio to support DER + // with java or just encode data with base64 + *content = SMIME_read_CMS(input, output); + if (*content == NULL) { + unsigned long err = ERR_peek_last_error(); + D(ERR, "Unable to parse input file: %s", ERR_lib_error_string(err)); + goto error_read; + } + + BIO_free(input); + + return 0; + +error_read: + BIO_free(input); +error: + return 1; +} + +int cert_verify(BIO *content, CMS_ContentInfo *content_info, X509_STORE *store, int *out_fd) { + BIO *output_temp; + int ret; + + *out_fd = create_temp_file(); + if (*out_fd < 0) { + D(ERR, "unable to create temporary file"); + return -1; + } + + output_temp = BIO_new_fd(*out_fd, BIO_NOCLOSE); + if (output_temp == NULL) { + D(ERR, "unable to create temporary bio"); + close(*out_fd); + return -1; + } + + ret = CMS_verify(content_info, NULL ,store, content, output_temp, 0); + + if (ret == 0) { + char buf[256]; + unsigned long err = ERR_peek_last_error(); + D(ERR, "Verification failed with reason: %s, %s", ERR_lib_error_string(err), ERR_error_string(err, buf)); + D(ERR, "Data used: content %p", content); + } + + ERR_clear_error(); + ERR_remove_state(0); + + BIO_free(output_temp); + + return ret; +} + +void cert_release(BIO *content, CMS_ContentInfo *info) { + BIO_free(content); + CMS_ContentInfo_free(info); +} + diff --git a/fastbootd/secure.h b/fastbootd/secure.h new file mode 100644 index 0000000..878a643 --- /dev/null +++ b/fastbootd/secure.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2009-2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _FASTBOOTD_SECURE_H +#define _FASTBOOTD_SECURE_H + +#include <openssl/ssl.h> +#include <openssl/x509.h> +#include <openssl/x509v3.h> +#include <openssl/pem.h> +#include <openssl/cms.h> + +void cert_init_crypto(); + +X509_STORE *cert_store_from_path(const char*stream); + +static inline void cert_release_store(X509_STORE *store) { + X509_STORE_free(store); +} + +int cert_read(int fd, CMS_ContentInfo **content, BIO **output); +int cert_verify(BIO *content, CMS_ContentInfo *content_info, X509_STORE *store, int *out_fd); +void cert_release(BIO *content, CMS_ContentInfo *info); + +#endif diff --git a/fastbootd/socket_client.c b/fastbootd/socket_client.c new file mode 100644 index 0000000..da636db --- /dev/null +++ b/fastbootd/socket_client.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2009-2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <cutils/sockets.h> +#include <poll.h> +#include <unistd.h> + +#include "utils.h" + +#define BUFFER_SIZE 256 + +#define STDIN_FD 0 +#define STDOUT_FD 1 +#define STDERR_FD 2 + +void run_socket_client() { + int fd; + char buffer[BUFFER_SIZE]; + int n; + struct pollfd fds[2]; + + fd = socket_local_client("fastbootd", + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_STREAM); + + if (fd < 0) { + fprintf(stderr, "ERROR: Unable to open fastbootd socket\n"); + return; + } + + fds[0].fd = STDIN_FD; + fds[0].events = POLLIN; + fds[1].fd = fd; + fds[1].events = POLLIN; + + while(true) { + if (poll(fds, 2, -1) <= 0) { + fprintf(stderr, "ERROR: socket error"); + return; + } + + if (fds[0].revents & POLLIN) { + if ((n = read(STDIN_FD, buffer, BUFFER_SIZE)) < 0) { + goto error; + } + + if (bulk_write(fd, buffer, n) < 0) { + goto error; + } + } + + if (fds[1].revents & POLLIN) { + if ((n = read(fd, buffer, BUFFER_SIZE)) < 0) { + goto error; + } + + if (bulk_write(STDOUT_FD, buffer, n) < 0) { + goto error; + } + } + } + +error: + fprintf(stderr, "Transport error\n"); +} diff --git a/fastbootd/socket_client.h b/fastbootd/socket_client.h new file mode 100644 index 0000000..4481ff2 --- /dev/null +++ b/fastbootd/socket_client.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2009-2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _FASTBOOTD_SOCKET_CLIENT_H +#define _FASTBOOTD_SOCKET_CLIENT_H + +void run_socket_client(); + +#endif diff --git a/fastbootd/transport.c b/fastbootd/transport.c index 01a5a8a..ce8f9d0 100644 --- a/fastbootd/transport.c +++ b/fastbootd/transport.c @@ -56,14 +56,14 @@ int transport_handle_download(struct transport_handle *thandle, size_t len) buffer = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (buffer == NULL) { - D(ERR, "mmap(%u) failed: %d %s", len, errno, strerror(errno)); + D(ERR, "mmap(%zu) failed: %d %s", len, errno, strerror(errno)); goto err; } while (n < len) { ret = thandle->transport->read(thandle, buffer + n, len - n); if (ret <= 0) { - D(WARN, "transport read failed, ret=%d %s", ret, strerror(-ret)); + D(WARN, "transport read failed, ret=%zd %s", ret, strerror(-ret)); break; } n += ret; @@ -99,6 +99,7 @@ static void *transport_data_thread(void *arg) } if (ret > 0) { buffer[ret] = 0; + //TODO: multiple threads protocol_handle_command(phandle, buffer); } } diff --git a/fastbootd/transport_socket.c b/fastbootd/transport_socket.c new file mode 100644 index 0000000..664d473 --- /dev/null +++ b/fastbootd/transport_socket.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2009-2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdlib.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <unistd.h> +#include <cutils/sockets.h> + +#include "debug.h" +#include "transport.h" +#include "utils.h" + + +#define container_of(ptr, type, member) \ + ((type*)((char*)(ptr) - offsetof(type, member))) + +#define SOCKET_WORKING 0 +#define SOCKET_STOPPED -1 + + +struct socket_transport { + struct transport transport; + + int fd; +}; + +struct socket_handle { + struct transport_handle handle; + + int fd; +}; + +void socket_close(struct transport_handle *thandle) +{ + struct socket_handle * handle = container_of(thandle, struct socket_handle, handle); + close(handle->fd); +} + +struct transport_handle *socket_connect(struct transport *transport) +{ + struct socket_handle *handle = calloc(sizeof(struct socket_handle), 1); + struct socket_transport *socket_transport = container_of(transport, struct socket_transport, transport); + struct sockaddr addr; + socklen_t alen = sizeof(addr); + + handle->fd = accept(socket_transport->fd, &addr, &alen); + + if (handle->fd < 0) { + D(WARN, "socket connect error"); + return NULL; + } + + D(DEBUG, "[ socket_thread - registering device ]"); + return &handle->handle; +} + +ssize_t socket_write(struct transport_handle *thandle, const void *data, size_t len) +{ + ssize_t ret; + struct socket_handle *handle = container_of(thandle, struct socket_handle, handle); + + D(DEBUG, "about to write (fd=%d, len=%zu)", handle->fd, len); + ret = bulk_write(handle->fd, data, len); + if (ret < 0) { + D(ERR, "ERROR: fd = %d, ret = %zd", handle->fd, ret); + return -1; + } + D(DEBUG, "[ socket_write done fd=%d ]", handle->fd); + return ret; +} + +ssize_t socket_read(struct transport_handle *thandle, void *data, size_t len) +{ + ssize_t ret; + struct socket_handle *handle = container_of(thandle, struct socket_handle, handle); + + D(DEBUG, "about to read (fd=%d, len=%zu)", handle->fd, len); + ret = bulk_read(handle->fd, data, len); + if (ret < 0) { + D(ERR, "ERROR: fd = %d, ret = %zd", handle->fd, ret); + return -1; + } + D(DEBUG, "[ socket_read done fd=%d ret=%zd]", handle->fd, ret); + return ret; +} + +static int listen_socket_init(struct socket_transport *socket_transport) +{ + int s = android_get_control_socket("fastbootd"); + + if (s < 0) { + D(WARN, "android_get_control_socket(fastbootd): %s\n", strerror(errno)); + return 0; + } + + if (listen(s, 4) < 0) { + D(WARN, "listen(control socket): %s\n", strerror(errno)); + return 0; + } + + socket_transport->fd = s; + + return 1; +} + + +int transport_socket_init() +{ + struct socket_transport *socket_transport = malloc(sizeof(struct socket_transport)); + + socket_transport->transport.connect = socket_connect; + socket_transport->transport.close = socket_close; + socket_transport->transport.read = socket_read; + socket_transport->transport.write = socket_write; + + if (!listen_socket_init(socket_transport)) { + D(ERR, "socket transport init failed"); + free(socket_transport); + return 0; + } + + transport_register(&socket_transport->transport); + return 1; +} + diff --git a/fastbootd/trigger.c b/fastbootd/trigger.c new file mode 100644 index 0000000..e63e64d --- /dev/null +++ b/fastbootd/trigger.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2009-2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <dlfcn.h> + +#include <hardware/hardware.h> +#include "debug.h" +#include "trigger.h" +#include "protocol.h" +#include "vendor_trigger.h" + +static const int version = 1; + +static struct vendor_trigger_t *triggers = NULL; + +int load_trigger() { + int err; + hw_module_t* module; + hw_device_t* device; + int libversion; + + err = hw_get_module(TRIGGER_MODULE_ID, (hw_module_t const**)&module); + + if (err == 0) { + err = module->methods->open(module, NULL, &device); + + if (err == 0) { + triggers = (struct vendor_trigger_t *) device; + } else { + D(WARN, "Libvendor load error"); + return 1; + } + } + else { + D(WARN, "Libvendor not load: %s", strerror(-err)); + return 0; + } + + if (triggers->check_version != NULL && + triggers->check_version(version, &libversion)) { + + triggers = NULL; + D(ERR, "Library report incompability"); + return 1; + } + D(INFO, "libvendortrigger loaded"); + + return 0; +} + +int trigger_oem_cmd(const char *arg, const char **response) { + if (triggers != NULL && triggers->oem_cmd != NULL) + return triggers->oem_cmd(arg, response); + return 0; +} + +int trigger_gpt_layout(struct GPT_content *table) { + if (triggers != NULL && triggers->gpt_layout != NULL) + return triggers->gpt_layout(table); + return 0; +} + diff --git a/fastbootd/trigger.h b/fastbootd/trigger.h new file mode 100644 index 0000000..404acb4 --- /dev/null +++ b/fastbootd/trigger.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2009-2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef __FASTBOOTD_TRIGGER_H_ +#define __FASTBOOTD_TRIGGER_H_ + +#include "commands/partitions.h" +#include "vendor_trigger.h" + +int load_trigger(); + +/* same as in struct triggers */ + +int trigger_gpt_layout(struct GPT_content *table); +int trigger_oem_cmd(const char *arg, const char **response); + +#endif diff --git a/fastbootd/usb_linux_client.c b/fastbootd/usb_linux_client.c index 111cf35..64420e9 100644 --- a/fastbootd/usb_linux_client.c +++ b/fastbootd/usb_linux_client.c @@ -30,6 +30,7 @@ #include "debug.h" #include "transport.h" +#include "utils.h" #define TRACE_TAG TRACE_USB @@ -50,8 +51,6 @@ #define USB_FFS_FASTBOOT_OUT USB_FFS_FASTBOOT_EP(ep1) #define USB_FFS_FASTBOOT_IN USB_FFS_FASTBOOT_EP(ep2) -#define READ_BUF_SIZE (16*1024) - #define container_of(ptr, type, member) \ ((type*)((char*)(ptr) - offsetof(type, member))) @@ -212,33 +211,13 @@ err: return -1; } -static ssize_t bulk_write(int bulk_in, const char *buf, size_t length) -{ - size_t count = 0; - ssize_t ret; - - do { - ret = TEMP_FAILURE_RETRY(write(bulk_in, buf + count, length - count)); - if (ret < 0) { - D(WARN, "[ bulk_read failed fd=%d length=%d errno=%d %s ]", - bulk_in, length, errno, strerror(errno)); - return -1; - } else { - count += ret; - } - } while (count < length); - - D(VERBOSE, "[ bulk_write done fd=%d ]", bulk_in); - return count; -} - static ssize_t usb_write(struct transport_handle *thandle, const void *data, size_t len) { ssize_t ret; struct transport *t = thandle->transport; struct usb_transport *usb_transport = container_of(t, struct usb_transport, transport); - D(DEBUG, "about to write (fd=%d, len=%d)", usb_transport->bulk_in, len); + D(DEBUG, "about to write (fd=%d, len=%zu)", usb_transport->bulk_in, len); ret = bulk_write(usb_transport->bulk_in, data, len); if (ret < 0) { D(ERR, "ERROR: fd = %d, ret = %zd", usb_transport->bulk_in, ret); @@ -248,37 +227,13 @@ static ssize_t usb_write(struct transport_handle *thandle, const void *data, siz return ret; } -static ssize_t bulk_read(int bulk_out, char *buf, size_t length) -{ - ssize_t ret; - size_t n = 0; - - while (n < length) { - size_t to_read = (length - n > READ_BUF_SIZE) ? READ_BUF_SIZE : length - n; - ret = TEMP_FAILURE_RETRY(read(bulk_out, buf + n, to_read)); - if (ret < 0) { - D(WARN, "[ bulk_read failed fd=%d length=%d errno=%d %s ]", - bulk_out, length, errno, strerror(errno)); - return ret; - } - n += ret; - if (ret < (ssize_t)to_read) { - D(VERBOSE, "bulk_read short read, ret=%zd to_read=%u n=%u length=%u", - ret, to_read, n, length); - break; - } - } - - return n; -} - ssize_t usb_read(struct transport_handle *thandle, void *data, size_t len) { ssize_t ret; struct transport *t = thandle->transport; struct usb_transport *usb_transport = container_of(t, struct usb_transport, transport); - D(DEBUG, "about to read (fd=%d, len=%d)", usb_transport->bulk_out, len); + D(DEBUG, "about to read (fd=%d, len=%zu)", usb_transport->bulk_out, len); ret = bulk_read(usb_transport->bulk_out, data, len); if (ret < 0) { D(ERR, "ERROR: fd = %d, ret = %zd", usb_transport->bulk_out, ret); diff --git a/fastbootd/utils.c b/fastbootd/utils.c new file mode 100644 index 0000000..bef2463 --- /dev/null +++ b/fastbootd/utils.c @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2009-2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <stdio.h> +#include <sys/ioctl.h> +#include <linux/fs.h> +#include <stdlib.h> +#include <cutils/properties.h> + +#include "utils.h" +#include "debug.h" + +#ifndef BLKDISCARD +#define BLKDISCARD _IO(0x12,119) +#endif + +#ifndef BLKSECDISCARD +#define BLKSECDISCARD _IO(0x12,125) +#endif + +#define READ_BUF_SIZE (16*1024) + +int get_stream_size(FILE *stream) { + int size; + fseek(stream, 0, SEEK_END); + size = ftell(stream); + fseek(stream, 0, SEEK_SET); + return size; +} + +uint64_t get_block_device_size(int fd) +{ + uint64_t size = 0; + int ret; + + ret = ioctl(fd, BLKGETSIZE64, &size); + + if (ret) + return 0; + + return size; +} + +uint64_t get_file_size(int fd) +{ + struct stat buf; + int ret; + int64_t computed_size; + + ret = fstat(fd, &buf); + if (ret) + return 0; + + if (S_ISREG(buf.st_mode)) + computed_size = buf.st_size; + else if (S_ISBLK(buf.st_mode)) + computed_size = get_block_device_size(fd); + else + computed_size = 0; + + return computed_size; +} + +uint64_t get_file_size64(int fd) +{ + struct stat64 buf; + int ret; + uint64_t computed_size; + + ret = fstat64(fd, &buf); + if (ret) + return 0; + + if (S_ISREG(buf.st_mode)) + computed_size = buf.st_size; + else if (S_ISBLK(buf.st_mode)) + computed_size = get_block_device_size(fd); + else + computed_size = 0; + + return computed_size; +} + + +char *strip(char *str) +{ + int n; + + n = strspn(str, " \t"); + str += n; + n = strcspn(str, " \t"); + str[n] = '\0'; + + return str; +} + +int wipe_block_device(int fd, int64_t len) +{ + uint64_t range[2]; + int ret; + + range[0] = 0; + range[1] = len; + ret = ioctl(fd, BLKSECDISCARD, &range); + if (ret < 0) { + range[0] = 0; + range[1] = len; + ret = ioctl(fd, BLKDISCARD, &range); + if (ret < 0) { + D(WARN, "Discard failed\n"); + return 1; + } else { + D(WARN, "Wipe via secure discard failed, used discard instead\n"); + return 0; + } + } + + return 0; +} + +int create_temp_file() { + char tempname[] = "/dev/fastboot_data_XXXXXX"; + int fd; + + fd = mkstemp(tempname); + if (fd < 0) + return -1; + + unlink(tempname); + + return fd; +} + +ssize_t bulk_write(int bulk_in, const char *buf, size_t length) +{ + size_t count = 0; + ssize_t ret; + + do { + ret = TEMP_FAILURE_RETRY(write(bulk_in, buf + count, length - count)); + if (ret < 0) { + D(WARN, "[ bulk_write failed fd=%d length=%zu errno=%d %s ]", + bulk_in, length, errno, strerror(errno)); + return -1; + } else { + count += ret; + } + } while (count < length); + + D(VERBOSE, "[ bulk_write done fd=%d ]", bulk_in); + return count; +} + +ssize_t bulk_read(int bulk_out, char *buf, size_t length) +{ + ssize_t ret; + size_t n = 0; + + while (n < length) { + size_t to_read = (length - n > READ_BUF_SIZE) ? READ_BUF_SIZE : length - n; + ret = TEMP_FAILURE_RETRY(read(bulk_out, buf + n, to_read)); + if (ret < 0) { + D(WARN, "[ bulk_read failed fd=%d length=%zu errno=%d %s ]", + bulk_out, length, errno, strerror(errno)); + return ret; + } + n += ret; + if (ret < (ssize_t)to_read) { + D(VERBOSE, "bulk_read short read, ret=%zd to_read=%zu n=%zu length=%zu", + ret, to_read, n, length); + break; + } + } + + return n; +} + +#define NAP_TIME 200 // 200 ms between polls +static int wait_for_property(const char *name, const char *desired_value, int maxwait) +{ + char value[PROPERTY_VALUE_MAX] = {'\0'}; + int maxnaps = (maxwait * 1000) / NAP_TIME; + + if (maxnaps < 1) { + maxnaps = 1; + } + + while (maxnaps-- > 0) { + usleep(NAP_TIME * 1000); + if (property_get(name, value, NULL)) { + if (desired_value == NULL || strcmp(value, desired_value) == 0) { + return 0; + } + } + } + return -1; /* failure */ +} + +int service_start(const char *service_name) +{ + int result = 0; + char property_value[PROPERTY_VALUE_MAX]; + + property_get(service_name, property_value, ""); + if (strcmp("running", property_value) != 0) { + D(INFO, "Starting %s", service_name); + property_set("ctl.start", service_name); + if (wait_for_property(service_name, "running", 5)) + result = -1; + } + + return result; +} + +int service_stop(const char *service_name) +{ + int result = 0; + + D(INFO, "Stopping MDNSD"); + property_set("ctl.stop", service_name); + if (wait_for_property(service_name, "stopped", 5)) + result = -1; + + return result; +} + +int ssh_server_start() +{ + return service_start("sshd"); +} diff --git a/fastbootd/utils.h b/fastbootd/utils.h new file mode 100644 index 0000000..3d98699 --- /dev/null +++ b/fastbootd/utils.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2009-2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _FASTBOOT_UTLIS_H +#define _FASTBOOT_UTILS_H + +#include <stdio.h> + +int get_stream_size(FILE *); + +char *strip(char *str); + +uint64_t get_file_size64(int fd); +uint64_t get_file_size(int fd); +uint64_t get_block_device_size(int fd); +int wipe_block_device(int fd, int64_t len); +int create_temp_file(); +ssize_t bulk_read(int bulk_out, char *buf, size_t length); +ssize_t bulk_write(int bulk_in, const char *buf, size_t length); +int service_start(const char *service_name); +int service_stop(const char *service_name); +int ssh_server_start(); + +#define ROUND_TO_PAGE(address,pagesize) ((address + pagesize - 1) & (~(pagesize - 1))) + +#define ROUND_UP(number,size) (((number + size - 1) / size) * size) + +#endif diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c index f432f6a..24ce806 100644 --- a/fs_mgr/fs_mgr.c +++ b/fs_mgr/fs_mgr.c @@ -28,11 +28,6 @@ #include <libgen.h> #include <time.h> #include <sys/swap.h> -/* XXX These need to be obtained from kernel headers. See b/9336527 */ -#define SWAP_FLAG_PREFER 0x8000 -#define SWAP_FLAG_PRIO_MASK 0x7fff -#define SWAP_FLAG_PRIO_SHIFT 0 -#define SWAP_FLAG_DISCARD 0x10000 #include <linux/loop.h> #include <private/android_filesystem_config.h> @@ -238,74 +233,16 @@ out: return f; } -/* Read a line of text till the next newline character. - * If no newline is found before the buffer is full, continue reading till a new line is seen, - * then return an empty buffer. This effectively ignores lines that are too long. - * On EOF, return null. - */ -static char *fs_getline(char *buf, int size, FILE *file) -{ - int cnt = 0; - int eof = 0; - int eol = 0; - int c; - - if (size < 1) { - return NULL; - } - - while (cnt < (size - 1)) { - c = getc(file); - if (c == EOF) { - eof = 1; - break; - } - - *(buf + cnt) = c; - cnt++; - - if (c == '\n') { - eol = 1; - break; - } - } - - /* Null terminate what we've read */ - *(buf + cnt) = '\0'; - - if (eof) { - if (cnt) { - return buf; - } else { - return NULL; - } - } else if (eol) { - return buf; - } else { - /* The line is too long. Read till a newline or EOF. - * If EOF, return null, if newline, return an empty buffer. - */ - while(1) { - c = getc(file); - if (c == EOF) { - return NULL; - } else if (c == '\n') { - *buf = '\0'; - return buf; - } - } - } -} - struct fstab *fs_mgr_read_fstab(const char *fstab_path) { FILE *fstab_file; int cnt, entries; - int len; - char line[256]; + ssize_t len; + size_t alloc_len = 0; + char *line = NULL; const char *delim = " \t"; char *save_ptr, *p; - struct fstab *fstab; + struct fstab *fstab = NULL; struct fstab_rec *recs; struct fs_mgr_flag_values flag_vals; #define FS_OPTIONS_LEN 1024 @@ -318,9 +255,8 @@ struct fstab *fs_mgr_read_fstab(const char *fstab_path) } entries = 0; - while (fs_getline(line, sizeof(line), fstab_file)) { + while ((len = getline(&line, &alloc_len, fstab_file)) != -1) { /* if the last character is a newline, shorten the string by 1 byte */ - len = strlen(line); if (line[len - 1] == '\n') { line[len - 1] = '\0'; } @@ -337,7 +273,7 @@ struct fstab *fs_mgr_read_fstab(const char *fstab_path) if (!entries) { ERROR("No entries found in fstab\n"); - return 0; + goto err; } /* Allocate and init the fstab structure */ @@ -349,9 +285,8 @@ struct fstab *fs_mgr_read_fstab(const char *fstab_path) fseek(fstab_file, 0, SEEK_SET); cnt = 0; - while (fs_getline(line, sizeof(line), fstab_file)) { + while ((len = getline(&line, &alloc_len, fstab_file)) != -1) { /* if the last character is a newline, shorten the string by 1 byte */ - len = strlen(line); if (line[len - 1] == '\n') { line[len - 1] = '\0'; } @@ -376,25 +311,25 @@ struct fstab *fs_mgr_read_fstab(const char *fstab_path) if (!(p = strtok_r(line, delim, &save_ptr))) { ERROR("Error parsing mount source\n"); - return 0; + goto err; } fstab->recs[cnt].blk_device = strdup(p); if (!(p = strtok_r(NULL, delim, &save_ptr))) { ERROR("Error parsing mount_point\n"); - return 0; + goto err; } fstab->recs[cnt].mount_point = strdup(p); if (!(p = strtok_r(NULL, delim, &save_ptr))) { ERROR("Error parsing fs_type\n"); - return 0; + goto err; } fstab->recs[cnt].fs_type = strdup(p); if (!(p = strtok_r(NULL, delim, &save_ptr))) { ERROR("Error parsing mount_flags\n"); - return 0; + goto err; } tmp_fs_options[0] = '\0'; fstab->recs[cnt].flags = parse_flags(p, mount_flags, NULL, @@ -409,7 +344,7 @@ struct fstab *fs_mgr_read_fstab(const char *fstab_path) if (!(p = strtok_r(NULL, delim, &save_ptr))) { ERROR("Error parsing fs_mgr_options\n"); - return 0; + goto err; } fstab->recs[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags, &flag_vals, NULL, 0); @@ -422,8 +357,15 @@ struct fstab *fs_mgr_read_fstab(const char *fstab_path) cnt++; } fclose(fstab_file); - + free(line); return fstab; + +err: + fclose(fstab_file); + free(line); + if (fstab) + fs_mgr_free_fstab(fstab); + return NULL; } void fs_mgr_free_fstab(struct fstab *fstab) @@ -442,7 +384,6 @@ void fs_mgr_free_fstab(struct fstab *fstab) free(fstab->recs[i].fs_options); free(fstab->recs[i].key_loc); free(fstab->recs[i].label); - i++; } /* Free the fstab_recs array created by calloc(3) */ @@ -576,6 +517,7 @@ int fs_mgr_mount_all(struct fstab *fstab) int encrypted = 0; int ret = -1; int mret; + int mount_errno; if (!fstab) { return ret; @@ -619,6 +561,9 @@ int fs_mgr_mount_all(struct fstab *fstab) continue; } + /* back up errno as partition_wipe clobbers the value */ + mount_errno = errno; + /* mount(2) returned an error, check if it's encrypted and deal with it */ if ((fstab->recs[i].fs_mgr_flags & MF_CRYPT) && !partition_wiped(fstab->recs[i].blk_device)) { @@ -627,14 +572,16 @@ int fs_mgr_mount_all(struct fstab *fstab) */ if (mount("tmpfs", fstab->recs[i].mount_point, "tmpfs", MS_NOATIME | MS_NOSUID | MS_NODEV, CRYPTO_TMPFS_OPTIONS) < 0) { - ERROR("Cannot mount tmpfs filesystem for encrypted fs at %s\n", - fstab->recs[i].mount_point); + ERROR("Cannot mount tmpfs filesystem for encrypted fs at %s error: %s\n", + fstab->recs[i].mount_point, strerror(errno)); goto out; } encrypted = 1; } else { - ERROR("Cannot mount filesystem on %s at %s\n", - fstab->recs[i].blk_device, fstab->recs[i].mount_point); + ERROR("Failed to mount an un-encryptable or wiped partition on" + "%s at %s options: %s error: %s\n", + fstab->recs[i].blk_device, fstab->recs[i].mount_point, + fstab->recs[i].fs_options, strerror(mount_errno)); goto out; } } @@ -703,8 +650,8 @@ int fs_mgr_do_mount(struct fstab *fstab, char *n_name, char *n_blk_device, } if (__mount(n_blk_device, m, fstab->recs[i].fs_type, fstab->recs[i].flags, fstab->recs[i].fs_options)) { - ERROR("Cannot mount filesystem on %s at %s\n", - n_blk_device, m); + ERROR("Cannot mount filesystem on %s at %s options: %s error: %s\n", + n_blk_device, m, fstab->recs[i].fs_options, strerror(errno)); goto out; } else { ret = 0; diff --git a/fs_mgr/fs_mgr_verity.c b/fs_mgr/fs_mgr_verity.c index 969eab2..1549316 100644 --- a/fs_mgr/fs_mgr_verity.c +++ b/fs_mgr/fs_mgr_verity.c @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <inttypes.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -178,7 +179,7 @@ static int read_verity_metadata(char *block_device, char **signature, char **tab goto out; } if (magic_number != VERITY_METADATA_MAGIC_NUMBER) { - ERROR("Couldn't find verity metadata at offset %llu!\n", device_length); + ERROR("Couldn't find verity metadata at offset %"PRIu64"!\n", device_length); goto out; } diff --git a/gpttool/gpttool.c b/gpttool/gpttool.c index 05d5177..d3f08fe 100644 --- a/gpttool/gpttool.c +++ b/gpttool/gpttool.c @@ -161,7 +161,7 @@ void show(struct ptable *ptbl) { struct efi_entry *entry = ptbl->entry; unsigned n, m; - char name[EFI_NAMELEN]; + char name[EFI_NAMELEN + 1]; fprintf(stderr,"ptn start block end block name\n"); fprintf(stderr,"---- ------------- ------------- --------------------\n"); diff --git a/include/backtrace/Backtrace.h b/include/backtrace/Backtrace.h new file mode 100644 index 0000000..bd4134c --- /dev/null +++ b/include/backtrace/Backtrace.h @@ -0,0 +1,116 @@ +/* + * 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. + */ + +#ifndef _BACKTRACE_BACKTRACE_H +#define _BACKTRACE_BACKTRACE_H + +#include <stdint.h> + +#include <string> +#include <vector> + +#include <backtrace/backtrace_constants.h> +#include <backtrace/BacktraceMap.h> + +struct backtrace_frame_data_t { + size_t num; // The current fame number. + uintptr_t pc; // The absolute pc. + uintptr_t sp; // The top of the stack. + size_t stack_size; // The size of the stack, zero indicate an unknown stack size. + const backtrace_map_t* map; // The map associated with the given pc. + std::string func_name; // The function name associated with this pc, NULL if not found. + uintptr_t func_offset; // pc relative to the start of the function, only valid if func_name is not NULL. +}; + +// Forward declarations. +class BacktraceImpl; + +class Backtrace { +public: + // Create the correct Backtrace object based on what is to be unwound. + // If pid < 0 or equals the current pid, then the Backtrace object + // corresponds to the current process. + // If pid < 0 or equals the current pid and tid >= 0, then the Backtrace + // object corresponds to a thread in the current process. + // If pid >= 0 and tid < 0, then the Backtrace object corresponds to a + // different process. + // Tracing a thread in a different process is not supported. + // If map is NULL, then create the map and manage it internally. + // If map is not NULL, the map is still owned by the caller. + static Backtrace* Create(pid_t pid, pid_t tid, BacktraceMap* map = NULL); + + virtual ~Backtrace(); + + // Get the current stack trace and store in the backtrace_ structure. + virtual bool Unwind(size_t num_ignore_frames); + + // Get the function name and offset into the function given the pc. + // If the string is empty, then no valid function name was found. + virtual std::string GetFunctionName(uintptr_t pc, uintptr_t* offset); + + // Find the map associated with the given pc. + virtual const backtrace_map_t* FindMap(uintptr_t pc); + + // Read the data at a specific address. + virtual bool ReadWord(uintptr_t ptr, uint32_t* out_value) = 0; + + // Create a string representing the formatted line of backtrace information + // for a single frame. + virtual std::string FormatFrameData(size_t frame_num); + virtual std::string FormatFrameData(const backtrace_frame_data_t* frame); + + pid_t Pid() { return pid_; } + pid_t Tid() { return tid_; } + size_t NumFrames() { return frames_.size(); } + + const backtrace_frame_data_t* GetFrame(size_t frame_num) { + if (frame_num >= frames_.size()) { + return NULL; + } + return &frames_[frame_num]; + } + + typedef std::vector<backtrace_frame_data_t>::iterator iterator; + iterator begin() { return frames_.begin(); } + iterator end() { return frames_.end(); } + + typedef std::vector<backtrace_frame_data_t>::const_iterator const_iterator; + const_iterator begin() const { return frames_.begin(); } + const_iterator end() const { return frames_.end(); } + + BacktraceMap* GetMap() { return map_; } + +protected: + Backtrace(BacktraceImpl* impl, pid_t pid, BacktraceMap* map); + + virtual bool VerifyReadWordArgs(uintptr_t ptr, uint32_t* out_value); + + bool BuildMap(); + + pid_t pid_; + pid_t tid_; + + BacktraceMap* map_; + bool map_shared_; + + std::vector<backtrace_frame_data_t> frames_; + + BacktraceImpl* impl_; + + friend class BacktraceImpl; +}; + +#endif // _BACKTRACE_BACKTRACE_H diff --git a/include/backtrace/BacktraceMap.h b/include/backtrace/BacktraceMap.h new file mode 100644 index 0000000..06da2f4 --- /dev/null +++ b/include/backtrace/BacktraceMap.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2014 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 _BACKTRACE_BACKTRACE_MAP_H +#define _BACKTRACE_BACKTRACE_MAP_H + +#include <stdint.h> +#ifdef USE_MINGW +// MINGW does not define these constants. +#define PROT_NONE 0 +#define PROT_READ 0x1 +#define PROT_WRITE 0x2 +#define PROT_EXEC 0x4 +#else +#include <sys/mman.h> +#endif + +#include <deque> +#include <string> + +struct backtrace_map_t { + uintptr_t start; + uintptr_t end; + int flags; + std::string name; +}; + +class BacktraceMap { +public: + static BacktraceMap* Create(pid_t pid); + + virtual ~BacktraceMap(); + + // Get the map data structure for the given address. + const backtrace_map_t* Find(uintptr_t addr); + + // The flags returned are the same flags as used by the mmap call. + // The values are PROT_*. + int GetFlags(uintptr_t pc) { + const backtrace_map_t* map = Find(pc); + if (map) { + return map->flags; + } + return PROT_NONE; + } + + bool IsReadable(uintptr_t pc) { return GetFlags(pc) & PROT_READ; } + bool IsWritable(uintptr_t pc) { return GetFlags(pc) & PROT_WRITE; } + bool IsExecutable(uintptr_t pc) { return GetFlags(pc) & PROT_EXEC; } + + typedef std::deque<backtrace_map_t>::iterator iterator; + iterator begin() { return maps_.begin(); } + iterator end() { return maps_.end(); } + + typedef std::deque<backtrace_map_t>::const_iterator const_iterator; + const_iterator begin() const { return maps_.begin(); } + const_iterator end() const { return maps_.end(); } + + virtual bool Build(); + +protected: + BacktraceMap(pid_t pid); + + virtual bool ParseLine(const char* line, backtrace_map_t* map); + + std::deque<backtrace_map_t> maps_; + pid_t pid_; +}; + +#endif // _BACKTRACE_BACKTRACE_MAP_H diff --git a/include/backtrace/backtrace_constants.h b/include/backtrace/backtrace_constants.h new file mode 100644 index 0000000..f8c1575 --- /dev/null +++ b/include/backtrace/backtrace_constants.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2014 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 _BACKTRACE_BACKTRACE_CONSTANTS_H +#define _BACKTRACE_BACKTRACE_CONSTANTS_H + +// When the pid to be traced is set to this value, then trace the current +// process. If the tid value is not BACKTRACE_NO_TID, then the specified +// thread from the current process will be traced. +#define BACKTRACE_CURRENT_PROCESS -1 +// When the tid to be traced is set to this value, then trace the specified +// current thread of the specified pid. +#define BACKTRACE_CURRENT_THREAD -1 + +#define MAX_BACKTRACE_FRAMES 64 + +#endif // _BACKTRACE_BACKTRACE_CONSTANTS_H diff --git a/include/cutils/atomic-arm64.h b/include/cutils/atomic-arm64.h new file mode 100644 index 0000000..4562ad0 --- /dev/null +++ b/include/cutils/atomic-arm64.h @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef ANDROID_CUTILS_ATOMIC_AARCH64_H +#define ANDROID_CUTILS_ATOMIC_AARCH64_H + +#include <stdint.h> + +#ifndef ANDROID_ATOMIC_INLINE +#define ANDROID_ATOMIC_INLINE inline __attribute__((always_inline)) +#endif + +/* + TODOAArch64: Revisit the below functions and check for potential + optimizations using assembly code or otherwise. +*/ + +extern ANDROID_ATOMIC_INLINE +void android_compiler_barrier(void) +{ + __asm__ __volatile__ ("" : : : "memory"); +} + +#if ANDROID_SMP == 0 +extern ANDROID_ATOMIC_INLINE +void android_memory_barrier(void) +{ + android_compiler_barrier(); +} +extern ANDROID_ATOMIC_INLINE +void android_memory_store_barrier(void) +{ + android_compiler_barrier(); +} +#else +extern ANDROID_ATOMIC_INLINE +void android_memory_barrier(void) +{ + __asm__ __volatile__ ("dmb ish" : : : "memory"); +} +extern ANDROID_ATOMIC_INLINE +void android_memory_store_barrier(void) +{ + __asm__ __volatile__ ("dmb ishst" : : : "memory"); +} +#endif + +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 ANDROID_ATOMIC_INLINE +int64_t android_atomic_acquire_load64(volatile const int64_t *ptr) +{ + int64_t value = *ptr; + android_memory_barrier(); + return value; +} + +extern ANDROID_ATOMIC_INLINE +int32_t android_atomic_release_load(volatile const int32_t *ptr) +{ + android_memory_barrier(); + return *ptr; +} + +extern ANDROID_ATOMIC_INLINE +int64_t android_atomic_release_load64(volatile const int64_t *ptr) +{ + android_memory_barrier(); + return *ptr; +} + +extern ANDROID_ATOMIC_INLINE +void android_atomic_acquire_store(int32_t value, volatile int32_t *ptr) +{ + *ptr = value; + android_memory_barrier(); +} + +extern ANDROID_ATOMIC_INLINE +void android_atomic_acquire_store64(int64_t value, volatile int64_t *ptr) +{ + *ptr = value; + android_memory_barrier(); +} + +extern ANDROID_ATOMIC_INLINE +void android_atomic_release_store(int32_t value, volatile int32_t *ptr) +{ + android_memory_barrier(); + *ptr = value; +} + +extern ANDROID_ATOMIC_INLINE +void android_atomic_release_store64(int64_t value, volatile int64_t *ptr) +{ + android_memory_barrier(); + *ptr = value; +} + +extern ANDROID_ATOMIC_INLINE +int android_atomic_cas(int32_t old_value, int32_t new_value, + volatile int32_t *ptr) +{ + return __sync_val_compare_and_swap(ptr, old_value, new_value) != old_value; +} + +extern ANDROID_ATOMIC_INLINE +int64_t android_atomic_cas64(int64_t old_value, int64_t new_value, + volatile int64_t *ptr) +{ + return __sync_val_compare_and_swap(ptr, old_value, new_value) != old_value; +} + +extern ANDROID_ATOMIC_INLINE +int android_atomic_acquire_cas(int32_t old_value, int32_t new_value, + volatile int32_t *ptr) +{ + int status = android_atomic_cas(old_value, new_value, ptr); + android_memory_barrier(); + return status; +} + +extern ANDROID_ATOMIC_INLINE +int64_t android_atomic_acquire_cas64(int64_t old_value, int64_t new_value, + volatile int64_t *ptr) +{ + int status = android_atomic_cas64(old_value, new_value, ptr); + android_memory_barrier(); + return status; +} + +extern ANDROID_ATOMIC_INLINE +int android_atomic_release_cas(int32_t old_value, int32_t new_value, + volatile int32_t *ptr) +{ + android_memory_barrier(); + return android_atomic_cas(old_value, new_value, ptr); +} + +extern ANDROID_ATOMIC_INLINE +int64_t android_atomic_release_cas64(int64_t old_value, int64_t new_value, + volatile int64_t *ptr) +{ + android_memory_barrier(); + return android_atomic_cas64(old_value, new_value, ptr); +} + +extern ANDROID_ATOMIC_INLINE +int32_t android_atomic_add(int32_t increment, volatile int32_t *ptr) +{ + int32_t prev, status; + android_memory_barrier(); + do { + prev = *ptr; + status = android_atomic_cas(prev, prev + increment, ptr); + } while (__builtin_expect(status != 0, 0)); + return prev; +} + +extern ANDROID_ATOMIC_INLINE +int32_t android_atomic_inc(volatile int32_t *addr) +{ + return android_atomic_add(1, addr); +} + +extern ANDROID_ATOMIC_INLINE +int32_t android_atomic_dec(volatile int32_t *addr) +{ + return android_atomic_add(-1, addr); +} + +extern ANDROID_ATOMIC_INLINE +int32_t android_atomic_and(int32_t value, volatile int32_t *ptr) +{ + int32_t prev, status; + android_memory_barrier(); + do { + prev = *ptr; + status = android_atomic_cas(prev, prev & value, ptr); + } while (__builtin_expect(status != 0, 0)); + return prev; +} + +extern ANDROID_ATOMIC_INLINE +int32_t android_atomic_or(int32_t value, volatile int32_t *ptr) +{ + int32_t prev, status; + android_memory_barrier(); + do { + prev = *ptr; + status = android_atomic_cas(prev, prev | value, ptr); + } while (__builtin_expect(status != 0, 0)); + return prev; +} + +#endif /* ANDROID_CUTILS_ATOMIC_AARCH64_H */ diff --git a/include/cutils/atomic-inline.h b/include/cutils/atomic-inline.h index 0b13138..ae79e00 100644 --- a/include/cutils/atomic-inline.h +++ b/include/cutils/atomic-inline.h @@ -43,7 +43,9 @@ extern "C" { # error "Must define ANDROID_SMP before including atomic-inline.h" #endif -#if defined(__arm__) +#if defined(__aarch64__) +#include <cutils/atomic-arm64.h> +#elif defined(__arm__) #include <cutils/atomic-arm.h> #elif defined(__i386__) || defined(__x86_64__) #include <cutils/atomic-x86.h> diff --git a/include/cutils/atomic.h b/include/cutils/atomic.h index ae42eb8..1787e34 100644 --- a/include/cutils/atomic.h +++ b/include/cutils/atomic.h @@ -80,6 +80,11 @@ int32_t android_atomic_or(int32_t value, volatile int32_t* addr); int32_t android_atomic_acquire_load(volatile const int32_t* addr); int32_t android_atomic_release_load(volatile const int32_t* addr); +#if defined (__LP64__) +int64_t android_atomic_acquire_load64(volatile const int64_t* addr); +int64_t android_atomic_release_load64(volatile const int64_t* addr); +#endif + /* * Perform an atomic store with "acquire" or "release" ordering. * @@ -89,6 +94,11 @@ int32_t android_atomic_release_load(volatile const int32_t* addr); void android_atomic_acquire_store(int32_t value, volatile int32_t* addr); void android_atomic_release_store(int32_t value, volatile int32_t* addr); +#if defined (__LP64__) +void android_atomic_acquire_store64(int64_t value, volatile int64_t* addr); +void android_atomic_release_store64(int64_t value, volatile int64_t* addr); +#endif + /* * Compare-and-set operation with "acquire" or "release" ordering. * @@ -106,6 +116,13 @@ int android_atomic_acquire_cas(int32_t oldvalue, int32_t newvalue, int android_atomic_release_cas(int32_t oldvalue, int32_t newvalue, volatile int32_t* addr); +#if defined (__LP64__) +int64_t android_atomic_acquire_cas64(int64_t old_value, int64_t new_value, + volatile int64_t *ptr); +int64_t android_atomic_release_cas64(int64_t old_value, int64_t new_value, + volatile int64_t *ptr); +#endif + /* * Aliases for code using an older version of this header. These are now * deprecated and should not be used. The definitions will be removed diff --git a/include/cutils/list.h b/include/cutils/list.h index 3881fc9..945729a 100644 --- a/include/cutils/list.h +++ b/include/cutils/list.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2008-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. @@ -44,9 +44,30 @@ struct listnode #define list_for_each_reverse(node, list) \ for (node = (list)->prev; node != (list); node = node->prev) -void list_init(struct listnode *list); -void list_add_tail(struct listnode *list, struct listnode *item); -void list_remove(struct listnode *item); +#define list_for_each_safe(node, next, list) \ + for (node = (list)->next, next = node->next; \ + node != (list); \ + node = next, next = node->next) + +static inline void list_init(struct listnode *node) +{ + node->next = node; + node->prev = node; +} + +static inline void list_add_tail(struct listnode *head, struct listnode *item) +{ + item->next = head; + item->prev = head->prev; + head->prev->next = item; + head->prev = item; +} + +static inline void list_remove(struct listnode *item) +{ + item->next->prev = item->prev; + item->prev->next = item->next; +} #define list_empty(list) ((list) == (list)->next) #define list_head(list) ((list)->next) diff --git a/include/log/log.h b/include/log/log.h index 7faddea..7f952ff 100644 --- a/include/log/log.h +++ b/include/log/log.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005 The Android Open Source Project + * Copyright (C) 2005-2014 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. @@ -28,17 +28,16 @@ #ifndef _LIBS_LOG_LOG_H #define _LIBS_LOG_LOG_H -#include <stdio.h> -#include <time.h> #include <sys/types.h> -#include <unistd.h> #ifdef HAVE_PTHREADS #include <pthread.h> #endif #include <stdarg.h> - -#include <log/uio.h> +#include <stdio.h> +#include <time.h> +#include <unistd.h> #include <log/logd.h> +#include <log/uio.h> #ifdef __cplusplus extern "C" { @@ -470,7 +469,8 @@ typedef enum { EVENT_TYPE_STRING = 2, EVENT_TYPE_LIST = 3, } AndroidEventLogType; - +#define sizeof_AndroidEventLogType sizeof(typeof_AndroidEventLogType) +#define typeof_AndroidEventLogType unsigned char #ifndef LOG_EVENT_INT #define LOG_EVENT_INT(_tag, _value) { \ @@ -540,7 +540,9 @@ typedef enum { #define android_logToFile(tag, file) (0) #define android_logToFd(tag, fd) (0) -typedef enum { +typedef enum log_id { + LOG_ID_MIN = 0, + LOG_ID_MAIN = 0, LOG_ID_RADIO = 1, LOG_ID_EVENTS = 2, @@ -548,6 +550,8 @@ typedef enum { LOG_ID_MAX } log_id_t; +#define sizeof_log_id_t sizeof(typeof_log_id_t) +#define typeof_log_id_t unsigned char /* * Send a simple string to the log. @@ -555,9 +559,8 @@ typedef enum { int __android_log_buf_write(int bufID, int prio, const char *tag, const char *text); int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fmt, ...); - #ifdef __cplusplus } #endif -#endif // _LIBS_CUTILS_LOG_H +#endif /* _LIBS_LOG_LOG_H */ diff --git a/include/log/log_read.h b/include/log/log_read.h new file mode 100644 index 0000000..861c192 --- /dev/null +++ b/include/log/log_read.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2013-2014 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 _LIBS_LOG_LOG_READ_H +#define _LIBS_LOG_LOG_READ_H + +#include <time.h> + +#define NS_PER_SEC 1000000000ULL +#ifdef __cplusplus +struct log_time : public timespec { +public: + log_time(timespec &T) + { + tv_sec = T.tv_sec; + tv_nsec = T.tv_nsec; + } + log_time(void) + { + } + log_time(clockid_t id) + { + clock_gettime(id, (timespec *) this); + } + log_time(const char *T) + { + const uint8_t *c = (const uint8_t *) T; + tv_sec = c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24); + tv_nsec = c[4] | (c[5] << 8) | (c[6] << 16) | (c[7] << 24); + } + bool operator== (const timespec &T) const + { + return (tv_sec == T.tv_sec) && (tv_nsec == T.tv_nsec); + } + bool operator!= (const timespec &T) const + { + return !(*this == T); + } + bool operator< (const timespec &T) const + { + return (tv_sec < T.tv_sec) + || ((tv_sec == T.tv_sec) && (tv_nsec < T.tv_nsec)); + } + bool operator>= (const timespec &T) const + { + return !(*this < T); + } + bool operator> (const timespec &T) const + { + return (tv_sec > T.tv_sec) + || ((tv_sec == T.tv_sec) && (tv_nsec > T.tv_nsec)); + } + bool operator<= (const timespec &T) const + { + return !(*this > T); + } + uint64_t nsec(void) const + { + return static_cast<uint64_t>(tv_sec) * NS_PER_SEC + tv_nsec; + } +}; +#else +typedef struct timespec log_time; +#endif + +#endif /* define _LIBS_LOG_LOG_READ_H */ diff --git a/include/log/logger.h b/include/log/logger.h index 04f3fb0..966397a 100644 --- a/include/log/logger.h +++ b/include/log/logger.h @@ -1,16 +1,21 @@ -/* utils/logger.h -** -** Copyright 2007, The Android Open Source Project +/* +** +** Copyright 2007-2014, The Android Open Source Project ** ** This file is dual licensed. It may be redistributed and/or modified ** under the terms of the Apache 2.0 License OR version 2 of the GNU ** General Public License. */ -#ifndef _UTILS_LOGGER_H -#define _UTILS_LOGGER_H +#ifndef _LIBS_LOG_LOGGER_H +#define _LIBS_LOG_LOGGER_H #include <stdint.h> +#include <log/log.h> + +#ifdef __cplusplus +extern "C" { +#endif /* * The userspace structure for version 1 of the logger_entry ABI. @@ -43,11 +48,6 @@ struct logger_entry_v2 { char msg[0]; /* the entry's payload */ }; -#define LOGGER_LOG_MAIN "log/main" -#define LOGGER_LOG_RADIO "log/radio" -#define LOGGER_LOG_EVENTS "log/events" -#define LOGGER_LOG_SYSTEM "log/system" - /* * The maximum size of the log entry payload that can be * written to the kernel logger driver. An attempt to write @@ -63,19 +63,108 @@ struct logger_entry_v2 { */ #define LOGGER_ENTRY_MAX_LEN (5*1024) -#ifdef HAVE_IOCTL +#define NS_PER_SEC 1000000000ULL + +struct log_msg { + union { + unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1]; + struct logger_entry_v2 entry; + struct logger_entry_v2 entry_v2; + struct logger_entry entry_v1; + struct { + unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1]; + log_id_t id; + } extra; + } __attribute__((aligned(4))); +#ifdef __cplusplus + /* Matching log_time operators */ + bool operator== (const log_msg &T) const + { + return (entry.sec == T.entry.sec) && (entry.nsec == T.entry.nsec); + } + bool operator!= (const log_msg &T) const + { + return !(*this == T); + } + bool operator< (const log_msg &T) const + { + return (entry.sec < T.entry.sec) + || ((entry.sec == T.entry.sec) + && (entry.nsec < T.entry.nsec)); + } + bool operator>= (const log_msg &T) const + { + return !(*this < T); + } + bool operator> (const log_msg &T) const + { + return (entry.sec > T.entry.sec) + || ((entry.sec == T.entry.sec) + && (entry.nsec > T.entry.nsec)); + } + bool operator<= (const log_msg &T) const + { + return !(*this > T); + } + uint64_t nsec(void) const + { + return static_cast<uint64_t>(entry.sec) * NS_PER_SEC + entry.nsec; + } + + /* packet methods */ + log_id_t id(void) + { + return extra.id; + } + char *msg(void) + { + return entry.hdr_size ? (char *) buf + entry.hdr_size : entry_v1.msg; + } + unsigned int len(void) + { + return (entry.hdr_size ? entry.hdr_size : sizeof(entry_v1)) + entry.len; + } +#endif +}; -#include <sys/ioctl.h> +struct logger; -#define __LOGGERIO 0xAE +log_id_t android_logger_get_id(struct logger *logger); -#define LOGGER_GET_LOG_BUF_SIZE _IO(__LOGGERIO, 1) /* size of log */ -#define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* used log len */ -#define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* next entry len */ -#define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */ -#define LOGGER_GET_VERSION _IO(__LOGGERIO, 5) /* abi version */ -#define LOGGER_SET_VERSION _IO(__LOGGERIO, 6) /* abi version */ +int android_logger_clear(struct logger *logger); +int android_logger_get_log_size(struct logger *logger); +int android_logger_get_log_readable_size(struct logger *logger); +int android_logger_get_log_version(struct logger *logger); + +struct logger_list; + +struct logger_list *android_logger_list_alloc(int mode, + unsigned int tail, + pid_t pid); +void android_logger_list_free(struct logger_list *logger_list); +/* In the purest sense, the following two are orthogonal interfaces */ +int android_logger_list_read(struct logger_list *logger_list, + struct log_msg *log_msg); + +/* Multiple log_id_t opens */ +struct logger *android_logger_open(struct logger_list *logger_list, + log_id_t id); +#define android_logger_close android_logger_free +/* Single log_id_t open */ +struct logger_list *android_logger_list_open(log_id_t id, + int mode, + unsigned int tail, + pid_t pid); +#define android_logger_list_close android_logger_list_free + +/* + * log_id_t helpers + */ +log_id_t android_name_to_log_id(const char *logName); +const char *android_log_id_to_name(log_id_t log_id); -#endif // HAVE_IOCTL +#ifdef __cplusplus +} +#endif -#endif /* _UTILS_LOGGER_H */ +#endif /* _LIBS_LOG_LOGGER_H */ diff --git a/include/log/uio.h b/include/log/uio.h index 01a74d2..a71f515 100644 --- a/include/log/uio.h +++ b/include/log/uio.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * Copyright (C) 2007-2014 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. @@ -31,8 +31,8 @@ extern "C" { #include <stddef.h> struct iovec { - const void* iov_base; - size_t iov_len; + void* iov_base; + size_t iov_len; }; extern int readv( int fd, struct iovec* vecs, int count ); diff --git a/include/mincrypt/dsa_sig.h b/include/mincrypt/dsa_sig.h new file mode 100644 index 0000000..b0d91cd --- /dev/null +++ b/include/mincrypt/dsa_sig.h @@ -0,0 +1,43 @@ +/* + * Copyright 2013 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. + */ + +#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_DSA_SIG_H_ +#define SYSTEM_CORE_INCLUDE_MINCRYPT_DSA_SIG_H_ + +#include "mincrypt/p256.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Returns 0 if input sig is not a valid ASN.1 sequence +int dsa_sig_unpack(unsigned char* sig, int sig_len, p256_int* r_int, p256_int* s_int); + +#ifdef __cplusplus +} +#endif + +#endif /* SYSTEM_CORE_INCLUDE_MINCRYPT_DSA_SIG_H_ */ diff --git a/include/mincrypt/hash-internal.h b/include/mincrypt/hash-internal.h index 96806f7..c813b44 100644 --- a/include/mincrypt/hash-internal.h +++ b/include/mincrypt/hash-internal.h @@ -1,8 +1,31 @@ -// Copyright 2007 Google Inc. All Rights Reserved. -// Author: mschilder@google.com (Marius Schilder) - -#ifndef SECURITY_UTIL_LITE_HASH_INTERNAL_H__ -#define SECURITY_UTIL_LITE_HASH_INTERNAL_H__ +/* + * Copyright 2007 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. + */ + +#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_HASH_INTERNAL_H_ +#define SYSTEM_CORE_INCLUDE_MINCRYPT_HASH_INTERNAL_H_ #include <stdint.h> @@ -37,4 +60,4 @@ typedef struct HASH_CTX { } #endif // __cplusplus -#endif // SECURITY_UTIL_LITE_HASH_INTERNAL_H__ +#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_HASH_INTERNAL_H_ diff --git a/include/mincrypt/p256.h b/include/mincrypt/p256.h new file mode 100644 index 0000000..465a1b9 --- /dev/null +++ b/include/mincrypt/p256.h @@ -0,0 +1,162 @@ +/* + * Copyright 2013 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. + */ + +#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_LITE_P256_H_ +#define SYSTEM_CORE_INCLUDE_MINCRYPT_LITE_P256_H_ + +// Collection of routines manipulating 256 bit unsigned integers. +// Just enough to implement ecdsa-p256 and related algorithms. + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define P256_BITSPERDIGIT 32 +#define P256_NDIGITS 8 +#define P256_NBYTES 32 + +typedef int p256_err; +typedef uint32_t p256_digit; +typedef int32_t p256_sdigit; +typedef uint64_t p256_ddigit; +typedef int64_t p256_sddigit; + +// Defining p256_int as struct to leverage struct assigment. +typedef struct { + p256_digit a[P256_NDIGITS]; +} p256_int; + +extern const p256_int SECP256r1_n; // Curve order +extern const p256_int SECP256r1_p; // Curve prime +extern const p256_int SECP256r1_b; // Curve param + +// Initialize a p256_int to zero. +void p256_init(p256_int* a); + +// Clear a p256_int to zero. +void p256_clear(p256_int* a); + +// Return bit. Index 0 is least significant. +int p256_get_bit(const p256_int* a, int index); + +// b := a % MOD +void p256_mod( + const p256_int* MOD, + const p256_int* a, + p256_int* b); + +// c := a * (top_b | b) % MOD +void p256_modmul( + const p256_int* MOD, + const p256_int* a, + const p256_digit top_b, + const p256_int* b, + p256_int* c); + +// b := 1 / a % MOD +// MOD best be SECP256r1_n +void p256_modinv( + const p256_int* MOD, + const p256_int* a, + p256_int* b); + +// b := 1 / a % MOD +// MOD best be SECP256r1_n +// Faster than p256_modinv() +void p256_modinv_vartime( + const p256_int* MOD, + const p256_int* a, + p256_int* b); + +// b := a << (n % P256_BITSPERDIGIT) +// Returns the bits shifted out of most significant digit. +p256_digit p256_shl(const p256_int* a, int n, p256_int* b); + +// b := a >> (n % P256_BITSPERDIGIT) +void p256_shr(const p256_int* a, int n, p256_int* b); + +int p256_is_zero(const p256_int* a); +int p256_is_odd(const p256_int* a); +int p256_is_even(const p256_int* a); + +// Returns -1, 0 or 1. +int p256_cmp(const p256_int* a, const p256_int *b); + +// c: = a - b +// Returns -1 on borrow. +int p256_sub(const p256_int* a, const p256_int* b, p256_int* c); + +// c := a + b +// Returns 1 on carry. +int p256_add(const p256_int* a, const p256_int* b, p256_int* c); + +// c := a + (single digit)b +// Returns carry 1 on carry. +int p256_add_d(const p256_int* a, p256_digit b, p256_int* c); + +// ec routines. + +// {out_x,out_y} := nG +void p256_base_point_mul(const p256_int *n, + p256_int *out_x, + p256_int *out_y); + +// {out_x,out_y} := n{in_x,in_y} +void p256_point_mul(const p256_int *n, + const p256_int *in_x, + const p256_int *in_y, + p256_int *out_x, + p256_int *out_y); + +// {out_x,out_y} := n1G + n2{in_x,in_y} +void p256_points_mul_vartime( + const p256_int *n1, const p256_int *n2, + const p256_int *in_x, const p256_int *in_y, + p256_int *out_x, p256_int *out_y); + +// Return whether point {x,y} is on curve. +int p256_is_valid_point(const p256_int* x, const p256_int* y); + +// Outputs big-endian binary form. No leading zero skips. +void p256_to_bin(const p256_int* src, uint8_t dst[P256_NBYTES]); + +// Reads from big-endian binary form, +// thus pre-pad with leading zeros if short. +void p256_from_bin(const uint8_t src[P256_NBYTES], p256_int* dst); + +#define P256_DIGITS(x) ((x)->a) +#define P256_DIGIT(x,y) ((x)->a[y]) + +#define P256_ZERO {{0}} +#define P256_ONE {{1}} + +#ifdef __cplusplus +} +#endif + +#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_LITE_P256_H_ diff --git a/include/mincrypt/p256_ecdsa.h b/include/mincrypt/p256_ecdsa.h new file mode 100644 index 0000000..da339fa --- /dev/null +++ b/include/mincrypt/p256_ecdsa.h @@ -0,0 +1,53 @@ +/* + * Copyright 2013 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. + */ + +#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_P256_ECDSA_H_ +#define SYSTEM_CORE_INCLUDE_MINCRYPT_P256_ECDSA_H_ + +// Using current directory as relative include path here since +// this code typically gets lifted into a variety of build systems +// and directory structures. +#include "p256.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Returns 0 if {r,s} is not a signature on message for +// public key {key_x,key_y}. +// +// Note: message is a p256_int. +// Convert from a binary string using p256_from_bin(). +int p256_ecdsa_verify(const p256_int* key_x, + const p256_int* key_y, + const p256_int* message, + const p256_int* r, const p256_int* s); + +#ifdef __cplusplus +} +#endif + +#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_P256_ECDSA_H_ diff --git a/include/mincrypt/rsa.h b/include/mincrypt/rsa.h index cc0e800..3d0556b 100644 --- a/include/mincrypt/rsa.h +++ b/include/mincrypt/rsa.h @@ -25,8 +25,8 @@ ** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef _EMBEDDED_RSA_H_ -#define _EMBEDDED_RSA_H_ +#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_RSA_H_ +#define SYSTEM_CORE_INCLUDE_MINCRYPT_RSA_H_ #include <inttypes.h> @@ -55,4 +55,4 @@ int RSA_verify(const RSAPublicKey *key, } #endif -#endif +#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_RSA_H_ diff --git a/include/mincrypt/sha.h b/include/mincrypt/sha.h index 120ddcb..ef60aab 100644 --- a/include/mincrypt/sha.h +++ b/include/mincrypt/sha.h @@ -1,8 +1,30 @@ -// Copyright 2005 Google Inc. All Rights Reserved. -// Author: mschilder@google.com (Marius Schilder) - -#ifndef SECURITY_UTIL_LITE_SHA1_H__ -#define SECURITY_UTIL_LITE_SHA1_H__ +/* + * Copyright 2005 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. + */ +#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_SHA1_H_ +#define SYSTEM_CORE_INCLUDE_MINCRYPT_SHA1_H_ #include <stdint.h> #include "hash-internal.h" @@ -27,4 +49,4 @@ const uint8_t* SHA_hash(const void* data, int len, uint8_t* digest); } #endif // __cplusplus -#endif // SECURITY_UTIL_LITE_SHA1_H__ +#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_SHA1_H_ diff --git a/include/mincrypt/sha256.h b/include/mincrypt/sha256.h index 0f3efb7..3a87c31 100644 --- a/include/mincrypt/sha256.h +++ b/include/mincrypt/sha256.h @@ -1,8 +1,31 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// Author: mschilder@google.com (Marius Schilder) - -#ifndef SECURITY_UTIL_LITE_SHA256_H__ -#define SECURITY_UTIL_LITE_SHA256_H__ +/* + * Copyright 2011 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. + */ + +#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_SHA256_H_ +#define SYSTEM_CORE_INCLUDE_MINCRYPT_SHA256_H_ #include <stdint.h> #include "hash-internal.h" @@ -26,4 +49,4 @@ const uint8_t* SHA256_hash(const void* data, int len, uint8_t* digest); } #endif // __cplusplus -#endif // SECURITY_UTIL_LITE_SHA256_H__ +#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_SHA256_H_ diff --git a/include/private/pixelflinger/ggl_context.h b/include/private/pixelflinger/ggl_context.h index 4864d5a..d43655c 100644 --- a/include/private/pixelflinger/ggl_context.h +++ b/include/private/pixelflinger/ggl_context.h @@ -121,7 +121,7 @@ template<bool> struct CTA; template<> struct CTA<true> { }; #define GGL_CONTEXT(con, c) context_t *con = static_cast<context_t *>(c) -#define GGL_OFFSETOF(field) int(&(((context_t*)0)->field)) +#define GGL_OFFSETOF(field) uintptr_t(&(((context_t*)0)->field)) #define GGL_INIT_PROC(p, f) p.f = ggl_ ## f; #define GGL_BETWEEN(x, L, H) (uint32_t((x)-(L)) <= ((H)-(L))) @@ -340,16 +340,18 @@ struct pixel_t { struct surface_t { union { - GGLSurface s; + GGLSurface s; + // Keep the following struct field types in line with the corresponding + // GGLSurface fields to avoid mismatches leading to errors. struct { - uint32_t reserved; - uint32_t width; - uint32_t height; - int32_t stride; - uint8_t* data; - uint8_t format; - uint8_t dirty; - uint8_t pad[2]; + GGLsizei reserved; + GGLuint width; + GGLuint height; + GGLint stride; + GGLubyte* data; + GGLubyte format; + GGLubyte dirty; + GGLubyte pad[2]; }; }; void (*read) (const surface_t* s, context_t* c, @@ -480,7 +482,7 @@ struct generated_tex_vars_t { uint32_t width; uint32_t height; uint32_t stride; - int32_t data; + uintptr_t data; int32_t dsdx; int32_t dtdx; int32_t spill[2]; diff --git a/include/private/pixelflinger/ggl_fixed.h b/include/private/pixelflinger/ggl_fixed.h index 217ec04..d0493f3 100644 --- a/include/private/pixelflinger/ggl_fixed.h +++ b/include/private/pixelflinger/ggl_fixed.h @@ -457,6 +457,69 @@ inline int64_t gglMulii(int32_t x, int32_t y) { return u.res; } +#elif defined(__aarch64__) + +// inline AArch64 implementations + +inline GGLfixed gglMulx(GGLfixed x, GGLfixed y, int shift) CONST; +inline GGLfixed gglMulx(GGLfixed x, GGLfixed y, int shift) +{ + GGLfixed result; + GGLfixed round; + + asm("mov %x[round], #1 \n" + "lsl %x[round], %x[round], %x[shift] \n" + "lsr %x[round], %x[round], #1 \n" + "smaddl %x[result], %w[x], %w[y],%x[round] \n" + "lsr %x[result], %x[result], %x[shift] \n" + : [round]"=&r"(round), [result]"=&r"(result) \ + : [x]"r"(x), [y]"r"(y), [shift] "r"(shift) \ + : + ); + return result; +} +inline GGLfixed gglMulAddx(GGLfixed x, GGLfixed y, GGLfixed a, int shift) CONST; +inline GGLfixed gglMulAddx(GGLfixed x, GGLfixed y, GGLfixed a, int shift) +{ + GGLfixed result; + asm("smull %x[result], %w[x], %w[y] \n" + "lsr %x[result], %x[result], %x[shift] \n" + "add %w[result], %w[result], %w[a] \n" + : [result]"=&r"(result) \ + : [x]"r"(x), [y]"r"(y), [a]"r"(a), [shift] "r"(shift) \ + : + ); + return result; +} + +inline GGLfixed gglMulSubx(GGLfixed x, GGLfixed y, GGLfixed a, int shift) CONST; +inline GGLfixed gglMulSubx(GGLfixed x, GGLfixed y, GGLfixed a, int shift) +{ + + GGLfixed result; + int rshift; + + asm("smull %x[result], %w[x], %w[y] \n" + "lsr %x[result], %x[result], %x[shift] \n" + "sub %w[result], %w[result], %w[a] \n" + : [result]"=&r"(result) \ + : [x]"r"(x), [y]"r"(y), [a]"r"(a), [shift] "r"(shift) \ + : + ); + return result; +} +inline int64_t gglMulii(int32_t x, int32_t y) CONST; +inline int64_t gglMulii(int32_t x, int32_t y) +{ + int64_t res; + asm("smull %x0, %w1, %w2 \n" + : "=r"(res) + : "%r"(x), "r"(y) + : + ); + return res; +} + #else // ---------------------------------------------------------------------- inline GGLfixed gglMulx(GGLfixed a, GGLfixed b, int shift) CONST; @@ -498,7 +561,7 @@ inline GGLfixed gglMulSubx(GGLfixed a, GGLfixed b, GGLfixed c) { inline int32_t gglClz(int32_t x) CONST; inline int32_t gglClz(int32_t x) { -#if (defined(__arm__) && !defined(__thumb__)) || defined(__mips__) +#if (defined(__arm__) && !defined(__thumb__)) || defined(__mips__) || defined(__aarch64__) return __builtin_clz(x); #else if (!x) return 32; @@ -554,6 +617,8 @@ inline GGLfixed gglClampx(GGLfixed c) // clamps to zero in one instruction, but gcc won't generate it and // replace it by a cmp + movlt (it's quite amazing actually). asm("bic %0, %1, %1, asr #31\n" : "=r"(c) : "r"(c)); +#elif defined(__aarch64__) + asm("bic %w0, %w1, %w1, asr #31\n" : "=r"(c) : "r"(c)); #else c &= ~(c>>31); #endif diff --git a/include/system/window.h b/include/system/window.h index 649bd71..588f9c6 100644 --- a/include/system/window.h +++ b/include/system/window.h @@ -22,7 +22,6 @@ #include <limits.h> #include <stdint.h> #include <string.h> -#include <sync/sync.h> #include <sys/cdefs.h> #include <system/graphics.h> #include <unistd.h> diff --git a/include/sysutils/NetlinkEvent.h b/include/sysutils/NetlinkEvent.h index f3501cf..c0a9418 100644 --- a/include/sysutils/NetlinkEvent.h +++ b/include/sysutils/NetlinkEvent.h @@ -36,6 +36,7 @@ public: const static int NlActionLinkUp; const static int NlActionAddressUpdated; const static int NlActionAddressRemoved; + const static int NlActionRdnss; NetlinkEvent(); virtual ~NetlinkEvent(); @@ -49,9 +50,10 @@ public: void dump(); protected: - bool parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, int rtasize); bool parseBinaryNetlinkMessage(char *buffer, int size); bool parseAsciiNetlinkMessage(char *buffer, int size); + bool parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, int rtasize); + bool parseNdUserOptMessage(struct nduseroptmsg *msg, int optsize); }; #endif diff --git a/include/sysutils/NetlinkListener.h b/include/sysutils/NetlinkListener.h index beb8bda..6e52c3b 100644 --- a/include/sysutils/NetlinkListener.h +++ b/include/sysutils/NetlinkListener.h @@ -21,7 +21,7 @@ class NetlinkEvent; class NetlinkListener : public SocketListener { - char mBuffer[64 * 1024]; + char mBuffer[64 * 1024] __attribute__((aligned(4))); int mFormat; public: diff --git a/include/sysutils/SocketClient.h b/include/sysutils/SocketClient.h index 85b58ef..1004f06 100644 --- a/include/sysutils/SocketClient.h +++ b/include/sysutils/SocketClient.h @@ -6,22 +6,23 @@ #include <pthread.h> #include <cutils/atomic.h> #include <sys/types.h> +#include <sys/uio.h> class SocketClient { int mSocket; bool mSocketOwned; pthread_mutex_t mWriteMutex; - /* Peer process ID */ + // Peer process ID pid_t mPid; - /* Peer user ID */ + // Peer user ID uid_t mUid; - /* Peer group ID */ + // Peer group ID gid_t mGid; - /* Reference count (starts at 1) */ + // Reference count (starts at 1) pthread_mutex_t mRefCountMutex; int mRefCount; @@ -38,12 +39,15 @@ public: pid_t getPid() const { return mPid; } uid_t getUid() const { return mUid; } gid_t getGid() const { return mGid; } - void setCmdNum(int cmdNum) { android_atomic_release_store(cmdNum, &mCmdNum); } + void setCmdNum(int cmdNum) { + android_atomic_release_store(cmdNum, &mCmdNum); + } int getCmdNum() { return mCmdNum; } // Send null-terminated C strings: int sendMsg(int code, const char *msg, bool addErrno); int sendMsg(int code, const char *msg, bool addErrno, bool useCmdNum); + int sendMsg(const char *msg); // Provides a mechanism to send a response code to the client. // Sends the code and a null character. @@ -56,6 +60,8 @@ public: // Sending binary data: int sendData(const void *data, int len); + // iovec contents not preserved through call + int sendDatav(struct iovec *iov, int iovcnt); // Optional reference counting. Reference count starts at 1. If // it's decremented to 0, it deletes itself. @@ -64,19 +70,18 @@ public: void incRef(); bool decRef(); // returns true at 0 (but note: SocketClient already deleted) - // return a new string in quotes with '\\' and '\"' escaped for "my arg" transmissions + // return a new string in quotes with '\\' and '\"' escaped for "my arg" + // transmissions static char *quoteArg(const char *arg); private: - // Send null-terminated C strings - int sendMsg(const char *msg); void init(int socket, bool owned, bool useCmdNum); - // Sending binary data. The caller should use make sure this is protected + // Sending binary data. The caller should make sure this is protected // from multiple threads entering simultaneously. - // returns 0 if successful, -1 if there is a 0 byte write and -2 if any other - // error occurred (use errno to get the error) - int sendDataLocked(const void *data, int len); + // returns 0 if successful, -1 if there is a 0 byte write or if any + // other error occurred (use errno to get the error) + int sendDataLockedv(struct iovec *iov, int iovcnt); }; typedef android::sysutils::List<SocketClient *> SocketClientCollection; diff --git a/include/sysutils/SocketClientCommand.h b/include/sysutils/SocketClientCommand.h new file mode 100644 index 0000000..746bc25 --- /dev/null +++ b/include/sysutils/SocketClientCommand.h @@ -0,0 +1,27 @@ +/* + * 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 _SOCKETCLIENTCOMMAND_H +#define _SOCKETCLIENTCOMMAND_H + +#include <sysutils/SocketClient.h> + +class SocketClientCommand { +public: + virtual ~SocketClientCommand() { } + virtual void runSocketCommand(SocketClient *client) = 0; +}; + +#endif diff --git a/include/sysutils/SocketListener.h b/include/sysutils/SocketListener.h index 8f56230..c204a0f 100644 --- a/include/sysutils/SocketListener.h +++ b/include/sysutils/SocketListener.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2008-2014 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. @@ -19,6 +19,7 @@ #include <pthread.h> #include <sysutils/SocketClient.h> +#include "SocketClientCommand.h" class SocketListener { bool mListen; @@ -41,10 +42,15 @@ public: void sendBroadcast(int code, const char *msg, bool addErrno); + void runOnEachSocket(SocketClientCommand *command); + + bool release(SocketClient *c) { return release(c, true); } + protected: virtual bool onDataAvailable(SocketClient *c) = 0; private: + bool release(SocketClient *c, bool wakeup); static void *threadStart(void *obj); void runListener(); void init(const char *socketName, int socketFd, bool listen, bool useCmdNum); diff --git a/include/utils/BitSet.h b/include/utils/BitSet.h index 19c03d1..f1d68a0 100644 --- a/include/utils/BitSet.h +++ b/include/utils/BitSet.h @@ -30,73 +30,103 @@ namespace android { struct BitSet32 { uint32_t value; - inline BitSet32() : value(0) { } + inline BitSet32() : value(0UL) { } explicit inline BitSet32(uint32_t value) : value(value) { } // Gets the value associated with a particular bit index. - static inline uint32_t valueForBit(uint32_t n) { return 0x80000000 >> n; } + static inline uint32_t valueForBit(uint32_t n) { return 0x80000000UL >> n; } // Clears the bit set. - inline void clear() { value = 0; } + inline void clear() { clear(value); } + + static inline void clear(uint32_t& value) { value = 0UL; } // Returns the number of marked bits in the set. - inline uint32_t count() const { return __builtin_popcount(value); } + inline uint32_t count() const { return count(value); } + + static inline uint32_t count(uint32_t value) { return __builtin_popcountl(value); } // Returns true if the bit set does not contain any marked bits. - inline bool isEmpty() const { return ! value; } + inline bool isEmpty() const { return isEmpty(value); } + + static inline bool isEmpty(uint32_t value) { return ! value; } // Returns true if the bit set does not contain any unmarked bits. - inline bool isFull() const { return value == 0xffffffff; } + inline bool isFull() const { return isFull(value); } + + static inline bool isFull(uint32_t value) { return value == 0xffffffffUL; } // Returns true if the specified bit is marked. - inline bool hasBit(uint32_t n) const { return value & valueForBit(n); } + inline bool hasBit(uint32_t n) const { return hasBit(value, n); } + + static inline bool hasBit(uint32_t value, uint32_t n) { return value & valueForBit(n); } // Marks the specified bit. - inline void markBit(uint32_t n) { value |= valueForBit(n); } + inline void markBit(uint32_t n) { markBit(value, n); } + + static inline void markBit (uint32_t& value, uint32_t n) { value |= valueForBit(n); } // Clears the specified bit. - inline void clearBit(uint32_t n) { value &= ~ valueForBit(n); } + inline void clearBit(uint32_t n) { clearBit(value, n); } + + static inline void clearBit(uint32_t& value, uint32_t n) { value &= ~ valueForBit(n); } // Finds the first marked bit in the set. // Result is undefined if all bits are unmarked. - inline uint32_t firstMarkedBit() const { return __builtin_clz(value); } + inline uint32_t firstMarkedBit() const { return firstMarkedBit(value); } + + static uint32_t firstMarkedBit(uint32_t value) { return __builtin_clzl(value); } // Finds the first unmarked bit in the set. // Result is undefined if all bits are marked. - inline uint32_t firstUnmarkedBit() const { return __builtin_clz(~ value); } + inline uint32_t firstUnmarkedBit() const { return firstUnmarkedBit(value); } + + static inline uint32_t firstUnmarkedBit(uint32_t value) { return __builtin_clzl(~ value); } // Finds the last marked bit in the set. // Result is undefined if all bits are unmarked. - inline uint32_t lastMarkedBit() const { return 31 - __builtin_ctz(value); } + inline uint32_t lastMarkedBit() const { return lastMarkedBit(value); } + + static inline uint32_t lastMarkedBit(uint32_t value) { return 31 - __builtin_ctzl(value); } // Finds the first marked bit in the set and clears it. Returns the bit index. // Result is undefined if all bits are unmarked. - inline uint32_t clearFirstMarkedBit() { - uint32_t n = firstMarkedBit(); - clearBit(n); + inline uint32_t clearFirstMarkedBit() { return clearFirstMarkedBit(value); } + + static inline uint32_t clearFirstMarkedBit(uint32_t& value) { + uint32_t n = firstMarkedBit(value); + clearBit(value, n); return n; } // Finds the first unmarked bit in the set and marks it. Returns the bit index. // Result is undefined if all bits are marked. - inline uint32_t markFirstUnmarkedBit() { - uint32_t n = firstUnmarkedBit(); - markBit(n); + inline uint32_t markFirstUnmarkedBit() { return markFirstUnmarkedBit(value); } + + static inline uint32_t markFirstUnmarkedBit(uint32_t& value) { + uint32_t n = firstUnmarkedBit(value); + markBit(value, n); return n; } // Finds the last marked bit in the set and clears it. Returns the bit index. // Result is undefined if all bits are unmarked. - inline uint32_t clearLastMarkedBit() { - uint32_t n = lastMarkedBit(); - clearBit(n); + inline uint32_t clearLastMarkedBit() { return clearLastMarkedBit(value); } + + static inline uint32_t clearLastMarkedBit(uint32_t& value) { + uint32_t n = lastMarkedBit(value); + clearBit(value, n); return n; } // Gets the index of the specified bit in the set, which is the number of // marked bits that appear before the specified bit. inline uint32_t getIndexOfBit(uint32_t n) const { - return __builtin_popcount(value & ~(0xffffffffUL >> n)); + return getIndexOfBit(value, n); + } + + static inline uint32_t getIndexOfBit(uint32_t value, uint32_t n) { + return __builtin_popcountl(value & ~(0xffffffffUL >> n)); } inline bool operator== (const BitSet32& other) const { return value == other.value; } @@ -119,6 +149,127 @@ struct BitSet32 { ANDROID_BASIC_TYPES_TRAITS(BitSet32) +// A simple set of 64 bits that can be individually marked or cleared. +struct BitSet64 { + uint64_t value; + + inline BitSet64() : value(0ULL) { } + explicit inline BitSet64(uint64_t value) : value(value) { } + + // Gets the value associated with a particular bit index. + static inline uint64_t valueForBit(uint32_t n) { return 0x8000000000000000ULL >> n; } + + // Clears the bit set. + inline void clear() { clear(value); } + + static inline void clear(uint64_t& value) { value = 0ULL; } + + // Returns the number of marked bits in the set. + inline uint32_t count() const { return count(value); } + + static inline uint32_t count(uint64_t value) { return __builtin_popcountll(value); } + + // Returns true if the bit set does not contain any marked bits. + inline bool isEmpty() const { return isEmpty(value); } + + static inline bool isEmpty(uint64_t value) { return ! value; } + + // Returns true if the bit set does not contain any unmarked bits. + inline bool isFull() const { return isFull(value); } + + static inline bool isFull(uint64_t value) { return value == 0xffffffffffffffffULL; } + + // Returns true if the specified bit is marked. + inline bool hasBit(uint32_t n) const { return hasBit(value, n); } + + static inline bool hasBit(uint64_t value, uint32_t n) { return value & valueForBit(n); } + + // Marks the specified bit. + inline void markBit(uint32_t n) { markBit(value, n); } + + static inline void markBit(uint64_t& value, uint32_t n) { value |= valueForBit(n); } + + // Clears the specified bit. + inline void clearBit(uint32_t n) { clearBit(value, n); } + + static inline void clearBit(uint64_t& value, uint32_t n) { value &= ~ valueForBit(n); } + + // Finds the first marked bit in the set. + // Result is undefined if all bits are unmarked. + inline uint32_t firstMarkedBit() const { return firstMarkedBit(value); } + + static inline uint32_t firstMarkedBit(uint64_t value) { return __builtin_clzll(value); } + + // Finds the first unmarked bit in the set. + // Result is undefined if all bits are marked. + inline uint32_t firstUnmarkedBit() const { return firstUnmarkedBit(value); } + + static inline uint32_t firstUnmarkedBit(uint64_t value) { return __builtin_clzll(~ value); } + + // Finds the last marked bit in the set. + // Result is undefined if all bits are unmarked. + inline uint32_t lastMarkedBit() const { return lastMarkedBit(value); } + + static inline uint32_t lastMarkedBit(uint64_t value) { return 63 - __builtin_ctzll(value); } + + // Finds the first marked bit in the set and clears it. Returns the bit index. + // Result is undefined if all bits are unmarked. + inline uint32_t clearFirstMarkedBit() { return clearFirstMarkedBit(value); } + + static inline uint32_t clearFirstMarkedBit(uint64_t& value) { + uint64_t n = firstMarkedBit(value); + clearBit(value, n); + return n; + } + + // Finds the first unmarked bit in the set and marks it. Returns the bit index. + // Result is undefined if all bits are marked. + inline uint32_t markFirstUnmarkedBit() { return markFirstUnmarkedBit(value); } + + static inline uint32_t markFirstUnmarkedBit(uint64_t& value) { + uint64_t n = firstUnmarkedBit(value); + markBit(value, n); + return n; + } + + // Finds the last marked bit in the set and clears it. Returns the bit index. + // Result is undefined if all bits are unmarked. + inline uint32_t clearLastMarkedBit() { return clearLastMarkedBit(value); } + + static inline uint32_t clearLastMarkedBit(uint64_t& value) { + uint64_t n = lastMarkedBit(value); + clearBit(value, n); + return n; + } + + // Gets the index of the specified bit in the set, which is the number of + // marked bits that appear before the specified bit. + inline uint32_t getIndexOfBit(uint32_t n) const { return getIndexOfBit(value, n); } + + static inline uint32_t getIndexOfBit(uint64_t value, uint32_t n) { + return __builtin_popcountll(value & ~(0xffffffffffffffffULL >> n)); + } + + inline bool operator== (const BitSet64& other) const { return value == other.value; } + inline bool operator!= (const BitSet64& other) const { return value != other.value; } + inline BitSet64 operator& (const BitSet64& other) const { + return BitSet64(value & other.value); + } + inline BitSet64& operator&= (const BitSet64& other) { + value &= other.value; + return *this; + } + inline BitSet64 operator| (const BitSet64& other) const { + return BitSet64(value | other.value); + } + inline BitSet64& operator|= (const BitSet64& other) { + value |= other.value; + return *this; + } +}; + +ANDROID_BASIC_TYPES_TRAITS(BitSet64) + } // namespace android #endif // UTILS_BITSET_H diff --git a/include/utils/CallStack.h b/include/utils/CallStack.h index 2056751..27e89f4 100644 --- a/include/utils/CallStack.h +++ b/include/utils/CallStack.h @@ -18,8 +18,9 @@ #define ANDROID_CALLSTACK_H #include <android/log.h> +#include <backtrace/backtrace_constants.h> #include <utils/String8.h> -#include <corkscrew/backtrace.h> +#include <utils/Vector.h> #include <stdint.h> #include <sys/types.h> @@ -31,42 +32,19 @@ class Printer; // Collect/print the call stack (function, file, line) traces for a single thread. class CallStack { public: - enum { - // Prune the lowest-most stack frames until we have at most MAX_DEPTH. - MAX_DEPTH = 31, - // Placeholder for specifying the current thread when updating the stack. - CURRENT_THREAD = -1, - }; - // Create an empty call stack. No-op. CallStack(); // Create a callstack with the current thread's stack trace. // Immediately dump it to logcat using the given logtag. - CallStack(const char* logtag, int32_t ignoreDepth=1, - int32_t maxDepth=MAX_DEPTH); - // Copy the existing callstack (no other side effects). - CallStack(const CallStack& rhs); + CallStack(const char* logtag, int32_t ignoreDepth=1); ~CallStack(); - // Copy the existing callstack (no other side effects). - CallStack& operator = (const CallStack& rhs); - - // Compare call stacks by their backtrace frame memory. - bool operator == (const CallStack& rhs) const; - bool operator != (const CallStack& rhs) const; - bool operator < (const CallStack& rhs) const; - bool operator >= (const CallStack& rhs) const; - bool operator > (const CallStack& rhs) const; - bool operator <= (const CallStack& rhs) const; - - // Get the PC address for the stack frame specified by index. - const void* operator [] (int index) const; - // Reset the stack frames (same as creating an empty call stack). - void clear(); + void clear() { mFrameLines.clear(); } // Immediately collect the stack traces for the specified thread. - void update(int32_t ignoreDepth=1, int32_t maxDepth=MAX_DEPTH, pid_t tid=CURRENT_THREAD); + // The default is to dump the stack of the current call. + void update(int32_t ignoreDepth=1, pid_t tid=BACKTRACE_CURRENT_THREAD); // Dump a stack trace to the log using the supplied logtag. void log(const char* logtag, @@ -83,11 +61,10 @@ public: void print(Printer& printer) const; // Get the count of stack frames that are in this call stack. - size_t size() const { return mCount; } + size_t size() const { return mFrameLines.size(); } private: - size_t mCount; - backtrace_frame_t mStack[MAX_DEPTH]; + Vector<String8> mFrameLines; }; }; // namespace android diff --git a/include/utils/Looper.h b/include/utils/Looper.h index 2e0651a..15c9891 100644 --- a/include/utils/Looper.h +++ b/include/utils/Looper.h @@ -22,17 +22,27 @@ #include <utils/KeyedVector.h> #include <utils/Timers.h> -#include <android/looper.h> - #include <sys/epoll.h> +namespace android { + /* - * Declare a concrete type for the NDK's looper forward declaration. + * NOTE: Since Looper is used to implement the NDK ALooper, the Looper + * enums and the signature of Looper_callbackFunc need to align with + * that implementation. */ -struct ALooper { -}; -namespace android { +/** + * For callback-based event loops, this is the prototype of the function + * that is called when a file descriptor event occurs. + * It is given the file descriptor it is associated with, + * a bitmask of the poll events that were triggered (typically EVENT_INPUT), + * and the data pointer that was originally supplied. + * + * Implementations should return 1 to continue receiving callbacks, or 0 + * to have this file descriptor and callback unregistered from the looper. + */ +typedef int (*Looper_callbackFunc)(int fd, int events, void* data); /** * A message that can be posted to a Looper. @@ -93,7 +103,7 @@ public: /** * Handles a poll event for the given file descriptor. * It is given the file descriptor it is associated with, - * a bitmask of the poll events that were triggered (typically ALOOPER_EVENT_INPUT), + * a bitmask of the poll events that were triggered (typically EVENT_INPUT), * and the data pointer that was originally supplied. * * Implementations should return 1 to continue receiving callbacks, or 0 @@ -102,34 +112,113 @@ public: virtual int handleEvent(int fd, int events, void* data) = 0; }; - /** - * Wraps a ALooper_callbackFunc function pointer. + * Wraps a Looper_callbackFunc function pointer. */ class SimpleLooperCallback : public LooperCallback { protected: virtual ~SimpleLooperCallback(); public: - SimpleLooperCallback(ALooper_callbackFunc callback); + SimpleLooperCallback(Looper_callbackFunc callback); virtual int handleEvent(int fd, int events, void* data); private: - ALooper_callbackFunc mCallback; + Looper_callbackFunc mCallback; }; - /** * A polling loop that supports monitoring file descriptor events, optionally * using callbacks. The implementation uses epoll() internally. * * A looper can be associated with a thread although there is no requirement that it must be. */ -class Looper : public ALooper, public RefBase { +class Looper : public RefBase { protected: virtual ~Looper(); public: + enum { + /** + * Result from Looper_pollOnce() and Looper_pollAll(): + * The poll was awoken using wake() before the timeout expired + * and no callbacks were executed and no other file descriptors were ready. + */ + POLL_WAKE = -1, + + /** + * Result from Looper_pollOnce() and Looper_pollAll(): + * One or more callbacks were executed. + */ + POLL_CALLBACK = -2, + + /** + * Result from Looper_pollOnce() and Looper_pollAll(): + * The timeout expired. + */ + POLL_TIMEOUT = -3, + + /** + * Result from Looper_pollOnce() and Looper_pollAll(): + * An error occurred. + */ + POLL_ERROR = -4, + }; + + /** + * Flags for file descriptor events that a looper can monitor. + * + * These flag bits can be combined to monitor multiple events at once. + */ + enum { + /** + * The file descriptor is available for read operations. + */ + EVENT_INPUT = 1 << 0, + + /** + * The file descriptor is available for write operations. + */ + EVENT_OUTPUT = 1 << 1, + + /** + * The file descriptor has encountered an error condition. + * + * The looper always sends notifications about errors; it is not necessary + * to specify this event flag in the requested event set. + */ + EVENT_ERROR = 1 << 2, + + /** + * The file descriptor was hung up. + * For example, indicates that the remote end of a pipe or socket was closed. + * + * The looper always sends notifications about hangups; it is not necessary + * to specify this event flag in the requested event set. + */ + EVENT_HANGUP = 1 << 3, + + /** + * The file descriptor is invalid. + * For example, the file descriptor was closed prematurely. + * + * The looper always sends notifications about invalid file descriptors; it is not necessary + * to specify this event flag in the requested event set. + */ + EVENT_INVALID = 1 << 4, + }; + + enum { + /** + * Option for Looper_prepare: this looper will accept calls to + * Looper_addFd() that do not have a callback (that is provide NULL + * for the callback). In this case the caller of Looper_pollOnce() + * or Looper_pollAll() MUST check the return from these functions to + * discover when data is available on such fds and process it. + */ + PREPARE_ALLOW_NON_CALLBACKS = 1<<0 + }; + /** * Creates a looper. * @@ -152,16 +241,16 @@ public: * If the timeout is zero, returns immediately without blocking. * If the timeout is negative, waits indefinitely until an event appears. * - * Returns ALOOPER_POLL_WAKE if the poll was awoken using wake() before + * Returns POLL_WAKE if the poll was awoken using wake() before * the timeout expired and no callbacks were invoked and no other file * descriptors were ready. * - * Returns ALOOPER_POLL_CALLBACK if one or more callbacks were invoked. + * Returns POLL_CALLBACK if one or more callbacks were invoked. * - * Returns ALOOPER_POLL_TIMEOUT if there was no data before the given + * Returns POLL_TIMEOUT if there was no data before the given * timeout expired. * - * Returns ALOOPER_POLL_ERROR if an error occurred. + * Returns POLL_ERROR if an error occurred. * * Returns a value >= 0 containing an identifier if its file descriptor has data * and it has no callback function (requiring the caller here to handle it). @@ -179,7 +268,7 @@ public: /** * Like pollOnce(), but performs all pending callbacks until all * data has been consumed or a file descriptor is available with no callback. - * This function will never return ALOOPER_POLL_CALLBACK. + * This function will never return POLL_CALLBACK. */ int pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData); inline int pollAll(int timeoutMillis) { @@ -200,8 +289,8 @@ public: * * "fd" is the file descriptor to be added. * "ident" is an identifier for this event, which is returned from pollOnce(). - * The identifier must be >= 0, or ALOOPER_POLL_CALLBACK if providing a non-NULL callback. - * "events" are the poll events to wake up on. Typically this is ALOOPER_EVENT_INPUT. + * The identifier must be >= 0, or POLL_CALLBACK if providing a non-NULL callback. + * "events" are the poll events to wake up on. Typically this is EVENT_INPUT. * "callback" is the function to call when there is an event on the file descriptor. * "data" is a private data pointer to supply to the callback. * @@ -211,7 +300,7 @@ public: * data on the file descriptor. It should execute any events it has pending, * appropriately reading from the file descriptor. The 'ident' is ignored in this case. * - * (2) If "callback" is NULL, the 'ident' will be returned by ALooper_pollOnce + * (2) If "callback" is NULL, the 'ident' will be returned by Looper_pollOnce * when its file descriptor has data available, requiring the caller to take * care of processing it. * @@ -225,7 +314,7 @@ public: * easier to avoid races when the callback is removed from a different thread. * See removeFd() for details. */ - int addFd(int fd, int ident, int events, ALooper_callbackFunc callback, void* data); + int addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data); int addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data); /** @@ -308,7 +397,7 @@ public: * If the thread already has a looper, it is returned. Otherwise, a new * one is created, associated with the thread, and returned. * - * The opts may be ALOOPER_PREPARE_ALLOW_NON_CALLBACKS or 0. + * The opts may be PREPARE_ALLOW_NON_CALLBACKS or 0. */ static sp<Looper> prepare(int opts); diff --git a/include/utils/ProcessCallStack.h b/include/utils/ProcessCallStack.h index 4a86869..32458b8 100644 --- a/include/utils/ProcessCallStack.h +++ b/include/utils/ProcessCallStack.h @@ -39,7 +39,7 @@ public: ~ProcessCallStack(); // Immediately collect the stack traces for all threads. - void update(int32_t maxDepth = CallStack::MAX_DEPTH); + void update(); // Print all stack traces to the log using the supplied logtag. void log(const char* logtag, android_LogPriority priority = ANDROID_LOG_DEBUG, diff --git a/include/ziparchive/zip_archive.h b/include/ziparchive/zip_archive.h new file mode 100644 index 0000000..1877494 --- /dev/null +++ b/include/ziparchive/zip_archive.h @@ -0,0 +1,181 @@ +/* + * 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. + */ + +/* + * Read-only access to Zip archives, with minimal heap allocation. + */ +#ifndef LIBZIPARCHIVE_ZIPARCHIVE_H_ +#define LIBZIPARCHIVE_ZIPARCHIVE_H_ + +#include <stdint.h> +#include <sys/types.h> +#include <utils/Compat.h> + +__BEGIN_DECLS + +/* Zip compression methods we support */ +enum { + kCompressStored = 0, // no compression + kCompressDeflated = 8, // standard deflate +}; + +struct ZipEntryName { + const char* name; + uint16_t name_length; +}; + +/* + * Represents information about a zip entry in a zip file. + */ +struct ZipEntry { + // Compression method: One of kCompressStored or + // kCompressDeflated. + uint16_t method; + + // Modification time. The zipfile format specifies + // that the first two little endian bytes contain the time + // and the last two little endian bytes contain the date. + uint32_t mod_time; + + // 1 if this entry contains a data descriptor segment, 0 + // otherwise. + uint8_t has_data_descriptor; + + // Crc32 value of this ZipEntry. This information might + // either be stored in the local file header or in a special + // Data descriptor footer at the end of the file entry. + uint32_t crc32; + + // Compressed length of this ZipEntry. Might be present + // either in the local file header or in the data descriptor + // footer. + uint32_t compressed_length; + + // Uncompressed length of this ZipEntry. Might be present + // either in the local file header or in the data descriptor + // footer. + uint32_t uncompressed_length; + + // The offset to the start of data for this ZipEntry. + off64_t offset; +}; + +typedef void* ZipArchiveHandle; + +/* + * Open a Zip archive, and sets handle to the value of the opaque + * handle for the file. This handle must be released by calling + * CloseArchive with this handle. + * + * Returns 0 on success, and negative values on failure. + */ +int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle); + +/* + * Like OpenArchive, but takes a file descriptor open for reading + * at the start of the file. The descriptor must be mappable (this does + * not allow access to a stream). + * + * Sets handle to the value of the opaque handle for this file descriptor. + * This handle must be released by calling CloseArchive with this handle. + * + * This function maps and scans the central directory and builds a table + * of entries for future lookups. + * + * "debugFileName" will appear in error messages, but is not otherwise used. + * + * Returns 0 on success, and negative values on failure. + */ +int32_t OpenArchiveFd(const int fd, const char* debugFileName, + ZipArchiveHandle *handle); + +/* + * Close archive, releasing resources associated with it. This will + * unmap the central directory of the zipfile and free all internal + * data structures associated with the file. It is an error to use + * this handle for any further operations without an intervening + * call to one of the OpenArchive variants. + */ +void CloseArchive(ZipArchiveHandle handle); + +/* + * Find an entry in the Zip archive, by name. |entryName| must be a null + * terminated string, and |data| must point to a writeable memory location. + * + * Returns 0 if an entry is found, and populates |data| with information + * about this entry. Returns negative values otherwise. + * + * It's important to note that |data->crc32|, |data->compLen| and + * |data->uncompLen| might be set to values from the central directory + * if this file entry contains a data descriptor footer. To verify crc32s + * and length, a call to VerifyCrcAndLengths must be made after entry data + * has been processed. + */ +int32_t FindEntry(const ZipArchiveHandle handle, const char* entryName, + ZipEntry* data); + +/* + * Start iterating over all entries of a zip file. The order of iteration + * is not guaranteed to be the same as the order of elements + * in the central directory but is stable for a given zip file. |cookie| + * must point to a writeable memory location, and will be set to the value + * of an opaque cookie which can be used to make one or more calls to + * Next. + * + * This method also accepts an optional prefix to restrict iteration to + * entry names that start with |prefix|. + * + * Returns 0 on success and negative values on failure. + */ +int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr, + const char* prefix); + +/* + * Advance to the next element in the zipfile in iteration order. + * + * Returns 0 on success, -1 if there are no more elements in this + * archive and lower negative values on failure. + */ +int32_t Next(void* cookie, ZipEntry* data, ZipEntryName *name); + +/* + * Uncompress and write an entry to an open file identified by |fd|. + * |entry->uncompressed_length| bytes will be written to the file at + * its current offset, and the file will be truncated at the end of + * the uncompressed data. + * + * Returns 0 on success and negative values on failure. + */ +int32_t ExtractEntryToFile(ZipArchiveHandle handle, ZipEntry* entry, int fd); + +/** + * Uncompress a given zip entry to the memory region at |begin| and of + * size |size|. This size is expected to be the same as the *declared* + * uncompressed length of the zip entry. It is an error if the *actual* + * number of uncompressed bytes differs from this number. + * + * Returns 0 on success and negative values on failure. + */ +int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry, + uint8_t* begin, uint32_t size); + +int GetFileDescriptor(const ZipArchiveHandle handle); + +const char* ErrorCodeString(int32_t error_code); + +__END_DECLS + +#endif // LIBZIPARCHIVE_ZIPARCHIVE_H_ diff --git a/init/Android.mk b/init/Android.mk index abfc68a..1f43ba6 100644 --- a/init/Android.mk +++ b/init/Android.mk @@ -10,7 +10,6 @@ LOCAL_SRC_FILES:= \ property_service.c \ util.c \ parser.c \ - logo.c \ keychords.c \ signal_handler.c \ init_parser.c \ diff --git a/init/builtins.c b/init/builtins.c index e8c8f91..e2932d5 100644 --- a/init/builtins.c +++ b/init/builtins.c @@ -797,12 +797,24 @@ int do_chmod(int nargs, char **args) { int do_restorecon(int nargs, char **args) { int i; + int ret = 0; for (i = 1; i < nargs; i++) { if (restorecon(args[i]) < 0) - return -errno; + ret = -errno; } - return 0; + return ret; +} + +int do_restorecon_recursive(int nargs, char **args) { + int i; + int ret = 0; + + for (i = 1; i < nargs; i++) { + if (restorecon_recursive(args[i]) < 0) + ret = -errno; + } + return ret; } int do_setsebool(int nargs, char **args) { diff --git a/init/devices.c b/init/devices.c index 1893642..f7df453 100644 --- a/init/devices.c +++ b/init/devices.c @@ -33,6 +33,7 @@ #include <selinux/selinux.h> #include <selinux/label.h> #include <selinux/android.h> +#include <selinux/avc.h> #include <private/android_filesystem_config.h> #include <sys/time.h> @@ -43,6 +44,7 @@ #include <cutils/uevent.h> #include "devices.h" +#include "ueventd_parser.h" #include "util.h" #include "log.h" @@ -529,8 +531,11 @@ static const char *parse_device_name(struct uevent *uevent, unsigned int len) name++; /* too-long names would overrun our buffer */ - if(strlen(name) > len) + if(strlen(name) > len) { + ERROR("DEVPATH=%s exceeds %u-character limit on filename; ignoring event\n", + name, len); return NULL; + } return name; } @@ -556,37 +561,76 @@ static void handle_block_device_event(struct uevent *uevent) uevent->major, uevent->minor, links); } +#define DEVPATH_LEN 96 + +static bool assemble_devpath(char *devpath, const char *dirname, + const char *devname) +{ + int s = snprintf(devpath, DEVPATH_LEN, "%s/%s", dirname, devname); + if (s < 0) { + ERROR("failed to assemble device path (%s); ignoring event\n", + strerror(errno)); + return false; + } else if (s >= DEVPATH_LEN) { + ERROR("%s/%s exceeds %u-character limit on path; ignoring event\n", + dirname, devname, DEVPATH_LEN); + return false; + } + return true; +} + +static void mkdir_recursive_for_devpath(const char *devpath) +{ + char dir[DEVPATH_LEN]; + char *slash; + + strcpy(dir, devpath); + slash = strrchr(dir, '/'); + *slash = '\0'; + mkdir_recursive(dir, 0755); +} + static void handle_generic_device_event(struct uevent *uevent) { char *base; const char *name; - char devpath[96] = {0}; + char devpath[DEVPATH_LEN] = {0}; char **links = NULL; name = parse_device_name(uevent, 64); if (!name) return; - if (!strncmp(uevent->subsystem, "usb", 3)) { + struct ueventd_subsystem *subsystem = + ueventd_subsystem_find_by_name(uevent->subsystem); + + if (subsystem) { + const char *devname; + + switch (subsystem->devname_src) { + case DEVNAME_UEVENT_DEVNAME: + devname = uevent->device_name; + break; + + case DEVNAME_UEVENT_DEVPATH: + devname = name; + break; + + default: + ERROR("%s subsystem's devpath option is not set; ignoring event\n", + uevent->subsystem); + return; + } + + if (!assemble_devpath(devpath, subsystem->dirname, devname)) + return; + mkdir_recursive_for_devpath(devpath); + } else if (!strncmp(uevent->subsystem, "usb", 3)) { if (!strcmp(uevent->subsystem, "usb")) { if (uevent->device_name) { - /* - * create device node provided by kernel if present - * see drivers/base/core.c - */ - char *p = devpath; - snprintf(devpath, sizeof(devpath), "/dev/%s", uevent->device_name); - /* skip leading /dev/ */ - p += 5; - /* build directories */ - while (*p) { - if (*p == '/') { - *p = 0; - make_dir(devpath, 0755); - *p = '/'; - } - p++; - } + if (!assemble_devpath(devpath, "/dev", uevent->device_name)) + return; + mkdir_recursive_for_devpath(devpath); } else { /* This imitates the file system that would be created @@ -830,6 +874,15 @@ void handle_device_fd() struct uevent uevent; parse_event(msg, &uevent); + if (sehandle && selinux_status_updated() > 0) { + struct selabel_handle *sehandle2; + sehandle2 = selinux_android_file_context_handle(); + if (sehandle2) { + selabel_close(sehandle); + sehandle = sehandle2; + } + } + handle_device_event(&uevent); handle_firmware_event(&uevent); } @@ -896,6 +949,7 @@ void device_init(void) sehandle = NULL; if (is_selinux_enabled() > 0) { sehandle = selinux_android_file_context_handle(); + selinux_status_open(true); } /* is 256K enough? udev uses 16MB! */ diff --git a/init/init.c b/init/init.c index bd7799e..00f4558 100644 --- a/init/init.c +++ b/init/init.c @@ -221,6 +221,9 @@ void service_start(struct service *svc, const char *dynamic_args) } rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon); + if (rc == 0 && !strcmp(scon, mycon)) { + ERROR("Warning! Service %s needs a SELinux domain defined; please fix!\n", svc->name); + } freecon(mycon); freecon(fcon); if (rc < 0) { @@ -250,14 +253,12 @@ void service_start(struct service *svc, const char *dynamic_args) for (ei = svc->envvars; ei; ei = ei->next) add_environment(ei->name, ei->value); - setsockcreatecon(scon); - for (si = svc->sockets; si; si = si->next) { int socket_type = ( !strcmp(si->type, "stream") ? SOCK_STREAM : (!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET)); int s = create_socket(si->name, socket_type, - si->perm, si->uid, si->gid); + si->perm, si->uid, si->gid, si->socketcon ?: scon); if (s >= 0) { publish_socket(si->name, s); } @@ -265,7 +266,6 @@ void service_start(struct service *svc, const char *dynamic_args) freecon(scon); scon = NULL; - setsockcreatecon(NULL); if (svc->ioprio_class != IoSchedClass_NONE) { if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) { @@ -623,7 +623,7 @@ static int mix_hwrng_into_linux_rng_action(int nargs, char **args) total_bytes_written += chunk_size; } - INFO("Mixed %d bytes from /dev/hw_random into /dev/urandom", + INFO("Mixed %zu bytes from /dev/hw_random into /dev/urandom", total_bytes_written); result = 0; @@ -657,29 +657,28 @@ static int console_init_action(int nargs, char **args) have_console = 1; close(fd); - if( load_565rle_image(INIT_IMAGE_FILE) ) { - fd = open("/dev/tty0", O_WRONLY); - if (fd >= 0) { - const char *msg; - msg = "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" // console is 40 cols x 30 lines - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - " A N D R O I D "; - write(fd, msg, strlen(msg)); - close(fd); - } + fd = open("/dev/tty0", O_WRONLY); + if (fd >= 0) { + const char *msg; + msg = "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" // console is 40 cols x 30 lines + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + " A N D R O I D "; + write(fd, msg, strlen(msg)); + close(fd); } + return 0; } @@ -868,6 +867,7 @@ struct selabel_handle* selinux_android_prop_context_handle(void) void selinux_init_all_handles(void) { sehandle = selinux_android_file_context_handle(); + selinux_android_set_sehandle(sehandle); sehandle_prop = selinux_android_prop_context_handle(); } @@ -1132,7 +1132,7 @@ int main(int argc, char **argv) continue; for (i = 0; i < fd_count; i++) { - if (ufds[i].revents == POLLIN) { + if (ufds[i].revents & POLLIN) { if (ufds[i].fd == get_property_set_fd()) handle_property_set_fd(); else if (ufds[i].fd == get_keychord_fd()) diff --git a/init/init.h b/init/init.h index aa6a4ab..736b75b 100644 --- a/init/init.h +++ b/init/init.h @@ -55,6 +55,7 @@ struct socketinfo { uid_t uid; gid_t gid; int perm; + const char *socketcon; }; struct svcenvinfo { @@ -132,10 +133,6 @@ void service_restart(struct service *svc); void service_start(struct service *svc, const char *dynamic_args); void property_changed(const char *name, const char *value); -#define INIT_IMAGE_FILE "/initlogo.rle" - -int load_565rle_image( char *file_name ); - extern struct selabel_handle *sehandle; extern struct selabel_handle *sehandle_prop; extern int selinux_reload_policy(void); diff --git a/init/init_parser.c b/init/init_parser.c index 776c699..f49e698 100644 --- a/init/init_parser.c +++ b/init/init_parser.c @@ -60,7 +60,7 @@ static void parse_line_action(struct parse_state *state, int nargs, char **args) #define KEYWORD(symbol, flags, nargs, func) \ [ K_##symbol ] = { #symbol, func, nargs + 1, flags, }, -struct { +static struct { const char *name; int (*func)(int nargs, char **args); unsigned char nargs; @@ -76,7 +76,7 @@ struct { #define kw_func(kw) (keyword_info[kw].func) #define kw_nargs(kw) (keyword_info[kw].nargs) -int lookup_keyword(const char *s) +static int lookup_keyword(const char *s) { switch (*s++) { case 'c': @@ -135,6 +135,7 @@ int lookup_keyword(const char *s) case 'r': if (!strcmp(s, "estart")) return K_restart; if (!strcmp(s, "estorecon")) return K_restorecon; + if (!strcmp(s, "estorecon_recursive")) return K_restorecon_recursive; if (!strcmp(s, "mdir")) return K_rmdir; if (!strcmp(s, "m")) return K_rm; break; @@ -169,7 +170,7 @@ int lookup_keyword(const char *s) return K_UNKNOWN; } -void parse_line_no_op(struct parse_state *state, int nargs, char **args) +static void parse_line_no_op(struct parse_state *state, int nargs, char **args) { } @@ -292,7 +293,7 @@ err: return -1; } -void parse_import(struct parse_state *state, int nargs, char **args) +static void parse_import(struct parse_state *state, int nargs, char **args) { struct listnode *import_list = state->priv; struct import *import; @@ -317,7 +318,7 @@ void parse_import(struct parse_state *state, int nargs, char **args) INFO("found import '%s', adding to import list", import->filename); } -void parse_new_section(struct parse_state *state, int kw, +static void parse_new_section(struct parse_state *state, int kw, int nargs, char **args) { printf("[ %s %s ]\n", args[0], @@ -552,12 +553,14 @@ void queue_all_property_triggers() if (length > PROP_NAME_MAX) { ERROR("property name too long in trigger %s", act->name); } else { + int ret; memcpy(prop_name, name, length); prop_name[length] = 0; /* does the property exist, and match the trigger value? */ - property_get(prop_name, value); - if (!strcmp(equals + 1, value) ||!strcmp(equals + 1, "*")) { + ret = property_get(prop_name, value); + if (ret > 0 && (!strcmp(equals + 1, value) || + !strcmp(equals + 1, "*"))) { action_add_queue_tail(act); } } @@ -771,7 +774,7 @@ static void parse_line_service(struct parse_state *state, int nargs, char **args svc->envvars = ei; break; } - case K_socket: {/* name type perm [ uid gid ] */ + case K_socket: {/* name type perm [ uid gid context ] */ struct socketinfo *si; if (nargs < 4) { parse_error(state, "socket option requires name, type, perm arguments\n"); @@ -794,6 +797,8 @@ static void parse_line_service(struct parse_state *state, int nargs, char **args si->uid = decode_uid(args[4]); if (nargs > 5) si->gid = decode_uid(args[5]); + if (nargs > 6) + si->socketcon = args[6]; si->next = svc->sockets; svc->sockets = si; break; diff --git a/init/keywords.h b/init/keywords.h index 5a44df3..97fe50c 100644 --- a/init/keywords.h +++ b/init/keywords.h @@ -17,6 +17,7 @@ int do_mount(int nargs, char **args); int do_powerctl(int nargs, char **args); int do_restart(int nargs, char **args); int do_restorecon(int nargs, char **args); +int do_restorecon_recursive(int nargs, char **args); int do_rm(int nargs, char **args); int do_rmdir(int nargs, char **args); int do_setcon(int nargs, char **args); @@ -71,6 +72,7 @@ enum { KEYWORD(powerctl, COMMAND, 1, do_powerctl) KEYWORD(restart, COMMAND, 1, do_restart) KEYWORD(restorecon, COMMAND, 1, do_restorecon) + KEYWORD(restorecon_recursive, COMMAND, 1, do_restorecon_recursive) KEYWORD(rm, COMMAND, 1, do_rm) KEYWORD(rmdir, COMMAND, 1, do_rmdir) KEYWORD(seclabel, OPTION, 0, 0) diff --git a/init/logo.c b/init/logo.c deleted file mode 100644 index 614224c..0000000 --- a/init/logo.c +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (C) 2008 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 <stdlib.h> -#include <unistd.h> -#include <fcntl.h> -#include <sys/mman.h> -#include <sys/stat.h> -#include <sys/types.h> - -#include <linux/fb.h> -#include <linux/kd.h> - -#include "log.h" - -#ifdef ANDROID -#include <cutils/memory.h> -#else -void android_memset16(void *_ptr, unsigned short val, unsigned count) -{ - unsigned short *ptr = _ptr; - count >>= 1; - while(count--) - *ptr++ = val; -} -#endif - -struct FB { - unsigned short *bits; - unsigned size; - int fd; - struct fb_fix_screeninfo fi; - struct fb_var_screeninfo vi; -}; - -#define fb_width(fb) ((fb)->vi.xres) -#define fb_height(fb) ((fb)->vi.yres) -#define fb_size(fb) ((fb)->vi.xres * (fb)->vi.yres * 2) - -static int fb_open(struct FB *fb) -{ - fb->fd = open("/dev/graphics/fb0", O_RDWR); - if (fb->fd < 0) - return -1; - - if (ioctl(fb->fd, FBIOGET_FSCREENINFO, &fb->fi) < 0) - goto fail; - if (ioctl(fb->fd, FBIOGET_VSCREENINFO, &fb->vi) < 0) - goto fail; - - fb->bits = mmap(0, fb_size(fb), PROT_READ | PROT_WRITE, - MAP_SHARED, fb->fd, 0); - if (fb->bits == MAP_FAILED) - goto fail; - - return 0; - -fail: - close(fb->fd); - return -1; -} - -static void fb_close(struct FB *fb) -{ - munmap(fb->bits, fb_size(fb)); - close(fb->fd); -} - -/* there's got to be a more portable way to do this ... */ -static void fb_update(struct FB *fb) -{ - fb->vi.yoffset = 1; - ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi); - fb->vi.yoffset = 0; - ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi); -} - -static int vt_set_mode(int graphics) -{ - int fd, r; - fd = open("/dev/tty0", O_RDWR | O_SYNC); - if (fd < 0) - return -1; - r = ioctl(fd, KDSETMODE, (void*) (graphics ? KD_GRAPHICS : KD_TEXT)); - close(fd); - return r; -} - -/* 565RLE image format: [count(2 bytes), rle(2 bytes)] */ - -int load_565rle_image(char *fn) -{ - struct FB fb; - struct stat s; - unsigned short *data, *bits, *ptr; - unsigned count, max; - int fd; - - if (vt_set_mode(1)) - return -1; - - fd = open(fn, O_RDONLY); - if (fd < 0) { - ERROR("cannot open '%s'\n", fn); - goto fail_restore_text; - } - - if (fstat(fd, &s) < 0) { - goto fail_close_file; - } - - data = mmap(0, s.st_size, PROT_READ, MAP_SHARED, fd, 0); - if (data == MAP_FAILED) - goto fail_close_file; - - if (fb_open(&fb)) - goto fail_unmap_data; - - max = fb_width(&fb) * fb_height(&fb); - ptr = data; - count = s.st_size; - bits = fb.bits; - while (count > 3) { - unsigned n = ptr[0]; - if (n > max) - break; - android_memset16(bits, ptr[1], n << 1); - bits += n; - max -= n; - ptr += 2; - count -= 4; - } - - munmap(data, s.st_size); - fb_update(&fb); - fb_close(&fb); - close(fd); - unlink(fn); - return 0; - -fail_unmap_data: - munmap(data, s.st_size); -fail_close_file: - close(fd); -fail_restore_text: - vt_set_mode(0); - return -1; -} - diff --git a/init/parser.h b/init/parser.h index 0a5802a..a58272a 100644 --- a/init/parser.h +++ b/init/parser.h @@ -33,7 +33,6 @@ struct parse_state void *priv; }; -int lookup_keyword(const char *s); void DUMP(void); int next_token(struct parse_state *state); void parse_error(struct parse_state *state, const char *fmt, ...); diff --git a/init/property_service.c b/init/property_service.c index 9ac2781..ac63377 100644 --- a/init/property_service.c +++ b/init/property_service.c @@ -81,6 +81,7 @@ struct { { "sys.powerctl", AID_SHELL, 0 }, { "service.", AID_SYSTEM, 0 }, { "wlan.", AID_SYSTEM, 0 }, + { "gps.", AID_GPS, 0 }, { "bluetooth.", AID_BLUETOOTH, 0 }, { "dhcp.", AID_SYSTEM, 0 }, { "dhcp.", AID_DHCP, 0 }, @@ -92,6 +93,7 @@ struct { { "persist.sys.", AID_SYSTEM, 0 }, { "persist.service.", AID_SYSTEM, 0 }, { "persist.security.", AID_SYSTEM, 0 }, + { "persist.gps.", AID_GPS, 0 }, { "persist.service.bdroid.", AID_BLUETOOTH, 0 }, { "selinux." , AID_SYSTEM, 0 }, { NULL, 0, 0 } @@ -166,7 +168,7 @@ static int check_mac_perms(const char *name, char *sctx) if (selabel_lookup(sehandle_prop, &tctx, name, 1) != 0) goto err; - if (selinux_check_access(sctx, tctx, class, perm, name) == 0) + if (selinux_check_access(sctx, tctx, class, perm, (void*) name) == 0) result = 1; freecon(tctx); @@ -380,7 +382,7 @@ void handle_property_set_fd() r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), 0)); if(r != sizeof(prop_msg)) { - ERROR("sys_prop: mis-match msg size received: %d expected: %d errno: %d\n", + ERROR("sys_prop: mis-match msg size received: %d expected: %zu errno: %d\n", r, sizeof(prop_msg), errno); close(s); return; @@ -437,10 +439,13 @@ void get_property_workspace(int *fd, int *sz) *sz = pa_workspace.size; } -static void load_properties(char *data) +static void load_properties(char *data, char *prefix) { char *key, *value, *eol, *sol, *tmp; + size_t plen; + if (prefix) + plen = strlen(prefix); sol = data; while((eol = strchr(sol, '\n'))) { key = sol; @@ -456,6 +461,9 @@ static void load_properties(char *data) tmp = value - 2; while((tmp > key) && isspace(*tmp)) *tmp-- = 0; + if (prefix && strncmp(key, prefix, plen)) + continue; + while(isspace(*value)) value++; tmp = eol - 2; while((tmp > value) && isspace(*tmp)) *tmp-- = 0; @@ -464,7 +472,7 @@ static void load_properties(char *data) } } -static void load_properties_from_file(const char *fn) +static void load_properties_from_file(const char *fn, char *prefix) { char *data; unsigned sz; @@ -472,7 +480,7 @@ static void load_properties_from_file(const char *fn) data = read_file(fn, &sz); if(data != 0) { - load_properties(data); + load_properties(data, prefix); free(data); } } @@ -514,7 +522,7 @@ static void load_persistent_properties() || (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", + ERROR("skipping insecure property file %s (uid=%u gid=%u nlink=%d mode=%o)\n", entry->d_name, sb.st_uid, sb.st_gid, sb.st_nlink, sb.st_mode); close(fd); continue; @@ -545,7 +553,7 @@ void property_init(void) void property_load_boot_defaults(void) { - load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT); + load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT, NULL); } int properties_inited(void) @@ -560,7 +568,7 @@ static void load_override_properties() { ret = property_get("ro.debuggable", debuggable); if (ret && (strcmp(debuggable, "1") == 0)) { - load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE); + load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE, NULL); } #endif /* ALLOW_LOCAL_PROP_OVERRIDE */ } @@ -582,13 +590,14 @@ void start_property_service(void) { int fd; - load_properties_from_file(PROP_PATH_SYSTEM_BUILD); - load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT); + load_properties_from_file(PROP_PATH_SYSTEM_BUILD, NULL); + load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT, NULL); + load_properties_from_file(PROP_PATH_FACTORY, "ro."); load_override_properties(); /* Read persistent properties after all default values have been loaded. */ load_persistent_properties(); - fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0); + fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0, NULL); if(fd < 0) return; fcntl(fd, F_SETFD, FD_CLOEXEC); fcntl(fd, F_SETFL, O_NONBLOCK); diff --git a/init/readme.txt b/init/readme.txt index 7a5997d..42a09cb 100644 --- a/init/readme.txt +++ b/init/readme.txt @@ -70,10 +70,13 @@ disabled setenv <name> <value> Set the environment variable <name> to <value> in the launched process. -socket <name> <type> <perm> [ <user> [ <group> ] ] +socket <name> <type> <perm> [ <user> [ <group> [ <context> ] ] ] Create a unix domain socket named /dev/socket/<name> and pass its fd to the launched process. <type> must be "dgram", "stream" or "seqpacket". User and group default to 0. + Context is the SELinux security context for the socket. + It defaults to the service security context, as specified by seclabel or + computed based on the service executable file security context. user <username> Change to username before exec'ing this service. @@ -189,12 +192,18 @@ mount <type> <device> <dir> [ <mountoption> ]* device by name. <mountoption>s include "ro", "rw", "remount", "noatime", ... -restorecon <path> +restorecon <path> [ <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. +restorecon_recursive <path> [ <path> ]* + Recursively restore the directory tree named by <path> to the + security contexts specified in the file_contexts configuration. + Do NOT use this with paths leading to shell-writable or app-writable + directories, e.g. /data/local/tmp, /data/data or any prefix thereof. + 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 diff --git a/init/signal_handler.c b/init/signal_handler.c index d31ad63..7e8e1a7 100644 --- a/init/signal_handler.c +++ b/init/signal_handler.c @@ -57,7 +57,15 @@ static int wait_for_one_process(int block) svc = service_find_by_pid(pid); if (!svc) { - ERROR("untracked pid %d exited\n", pid); + if (WIFEXITED(status)) { + ERROR("untracked pid %d exited with status %d\n", pid, WEXITSTATUS(status)); + } else if (WIFSIGNALED(status)) { + ERROR("untracked pid %d killed by signal %d\n", pid, WTERMSIG(status)); + } else if (WIFSTOPPED(status)) { + ERROR("untracked pid %d stopped by signal %d\n", pid, WSTOPSIG(status)); + } else { + ERROR("untracked pid %d state changed\n", pid); + } return 0; } diff --git a/init/ueventd.c b/init/ueventd.c index a41c31e..3d01836 100644 --- a/init/ueventd.c +++ b/init/ueventd.c @@ -94,7 +94,7 @@ int ueventd_main(int argc, char **argv) nr = poll(&ufd, 1, -1); if (nr <= 0) continue; - if (ufd.revents == POLLIN) + if (ufd.revents & POLLIN) handle_device_fd(); } } diff --git a/init/ueventd.h b/init/ueventd.h index 9066e47..0a454c5 100644 --- a/init/ueventd.h +++ b/init/ueventd.h @@ -17,6 +17,21 @@ #ifndef _INIT_UEVENTD_H_ #define _INIT_UEVENTD_H_ +#include <cutils/list.h> +#include <sys/types.h> + +struct ueventd_subsystem { + struct listnode slist; + + const char *name; + enum { + DEVNAME_UNKNOWN = 0, + DEVNAME_UEVENT_DEVNAME, + DEVNAME_UEVENT_DEVPATH, + } devname_src; + const char *dirname; +}; + int ueventd_main(int argc, char **argv); #endif diff --git a/init/ueventd_keywords.h b/init/ueventd_keywords.h new file mode 100644 index 0000000..88e8f01 --- /dev/null +++ b/init/ueventd_keywords.h @@ -0,0 +1,15 @@ +#ifndef KEYWORD +#define __MAKE_KEYWORD_ENUM__ +#define KEYWORD(symbol, flags, nargs) K_##symbol, +enum { + K_UNKNOWN, +#endif + KEYWORD(subsystem, SECTION, 1) + KEYWORD(devname, OPTION, 1) + KEYWORD(dirname, OPTION, 1) +#ifdef __MAKE_KEYWORD_ENUM__ + KEYWORD_COUNT, +}; +#undef __MAKE_KEYWORD_ENUM__ +#undef KEYWORD +#endif diff --git a/init/ueventd_parser.c b/init/ueventd_parser.c index 3e60df5..e447006 100644 --- a/init/ueventd_parser.c +++ b/init/ueventd_parser.c @@ -14,18 +14,189 @@ * limitations under the License. */ +#include <ctype.h> +#include <errno.h> #include <stdio.h> #include <unistd.h> #include <stdarg.h> +#include <stdlib.h> #include <string.h> +#include "ueventd.h" #include "ueventd_parser.h" #include "parser.h" #include "log.h" #include "util.h" +static list_declare(subsystem_list); + static void parse_line_device(struct parse_state *state, int nargs, char **args); +#define SECTION 0x01 +#define OPTION 0x02 + +#include "ueventd_keywords.h" + +#define KEYWORD(symbol, flags, nargs) \ + [ K_##symbol ] = { #symbol, nargs + 1, flags, }, + +static struct { + const char *name; + unsigned char nargs; + unsigned char flags; +} keyword_info[KEYWORD_COUNT] = { + [ K_UNKNOWN ] = { "unknown", 0, 0 }, +#include "ueventd_keywords.h" +}; +#undef KEYWORD + +#define kw_is(kw, type) (keyword_info[kw].flags & (type)) +#define kw_nargs(kw) (keyword_info[kw].nargs) + +static int lookup_keyword(const char *s) +{ + switch (*s++) { + case 'd': + if (!strcmp(s, "evname")) return K_devname; + if (!strcmp(s, "irname")) return K_dirname; + break; + case 's': + if (!strcmp(s, "ubsystem")) return K_subsystem; + break; + } + return K_UNKNOWN; +} + +static void parse_line_no_op(struct parse_state *state __attribute__((unused)), + int nargs __attribute__((unused)), char **args __attribute__((unused))) +{ +} + +static int valid_name(const char *name) +{ + while (*name) { + if (!isalnum(*name) && (*name != '_') && (*name != '-')) { + return 0; + } + name++; + } + return 1; +} + +struct ueventd_subsystem *ueventd_subsystem_find_by_name(const char *name) +{ + struct listnode *node; + struct ueventd_subsystem *s; + + list_for_each(node, &subsystem_list) { + s = node_to_item(node, struct ueventd_subsystem, slist); + if (!strcmp(s->name, name)) { + return s; + } + } + return 0; +} + +static void *parse_subsystem(struct parse_state *state, + int nargs __attribute__((unused)), char **args) +{ + struct ueventd_subsystem *s; + + if (!valid_name(args[1])) { + parse_error(state, "invalid subsystem name '%s'\n", args[1]); + return 0; + } + + s = ueventd_subsystem_find_by_name(args[1]); + if (s) { + parse_error(state, "ignored duplicate definition of subsystem '%s'\n", + args[1]); + return 0; + } + + s = calloc(1, sizeof(*s)); + if (!s) { + parse_error(state, "out of memory\n"); + return 0; + } + s->name = args[1]; + s->dirname = "/dev"; + list_add_tail(&subsystem_list, &s->slist); + return s; +} + +static void parse_line_subsystem(struct parse_state *state, int nargs, + char **args) +{ + struct ueventd_subsystem *s = state->context; + int kw; + + if (nargs == 0) { + return; + } + + kw = lookup_keyword(args[0]); + switch (kw) { + case K_devname: + if (!strcmp(args[1], "uevent_devname")) + s->devname_src = DEVNAME_UEVENT_DEVNAME; + else if (!strcmp(args[1], "uevent_devpath")) + s->devname_src = DEVNAME_UEVENT_DEVPATH; + else + parse_error(state, "invalid devname '%s'\n", args[1]); + break; + + case K_dirname: + if (args[1][0] == '/') + s->dirname = args[1]; + else + parse_error(state, "dirname '%s' does not start with '/'\n", + args[1]); + break; + + default: + parse_error(state, "invalid option '%s'\n", args[0]); + } +} + +static void parse_new_section(struct parse_state *state, int kw, + int nargs, char **args) +{ + printf("[ %s %s ]\n", args[0], + nargs > 1 ? args[1] : ""); + + switch(kw) { + case K_subsystem: + state->context = parse_subsystem(state, nargs, args); + if (state->context) { + state->parse_line = parse_line_subsystem; + return; + } + break; + } + state->parse_line = parse_line_no_op; +} + +static void parse_line(struct parse_state *state, char **args, int nargs) +{ + int kw = lookup_keyword(args[0]); + int kw_nargs = kw_nargs(kw); + + if (nargs < kw_nargs) { + parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1, + kw_nargs > 2 ? "arguments" : "argument"); + return; + } + + if (kw_is(kw, SECTION)) { + parse_new_section(state, kw, nargs, args); + } else if (kw_is(kw, OPTION)) { + state->parse_line(state, nargs, args); + } else { + parse_line_device(state, nargs, args); + } +} + static void parse_config(const char *fn, char *s) { struct parse_state state; @@ -36,18 +207,19 @@ static void parse_config(const char *fn, char *s) state.line = 1; state.ptr = s; state.nexttoken = 0; - state.parse_line = parse_line_device; + state.parse_line = parse_line_no_op; for (;;) { int token = next_token(&state); switch (token) { case T_EOF: - state.parse_line(&state, 0, 0); + parse_line(&state, args, nargs); return; case T_NEWLINE: if (nargs) { - state.parse_line(&state, nargs, args); + parse_line(&state, args, nargs); nargs = 0; } + state.line++; break; case T_TEXT: if (nargs < UEVENTD_PARSER_MAXARGS) { @@ -69,7 +241,8 @@ int ueventd_parse_config_file(const char *fn) return 0; } -static void parse_line_device(struct parse_state* state, int nargs, char **args) +static void parse_line_device(struct parse_state *state __attribute__((unused)), + int nargs, char **args) { set_device_permission(nargs, args); } diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h index 3684285..907cc49 100644 --- a/init/ueventd_parser.h +++ b/init/ueventd_parser.h @@ -17,9 +17,12 @@ #ifndef _INIT_UEVENTD_PARSER_H_ #define _INIT_UEVENTD_PARSER_H_ +#include "ueventd.h" + #define UEVENTD_PARSER_MAXARGS 5 int ueventd_parse_config_file(const char *fn); void set_device_permission(int nargs, char **args); +struct ueventd_subsystem *ueventd_subsystem_find_by_name(const char *name); #endif diff --git a/init/util.c b/init/util.c index 1908b3a..e772342 100644 --- a/init/util.c +++ b/init/util.c @@ -25,6 +25,7 @@ #include <ftw.h> #include <selinux/label.h> +#include <selinux/android.h> #include <sys/stat.h> #include <sys/types.h> @@ -84,11 +85,15 @@ unsigned int decode_uid(const char *s) * daemon. We communicate the file descriptor's value via the environment * variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo"). */ -int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid) +int create_socket(const char *name, int type, mode_t perm, uid_t uid, + gid_t gid, const char *socketcon) { struct sockaddr_un addr; int fd, ret; - char *secon; + char *filecon; + + if (socketcon) + setsockcreatecon(socketcon); fd = socket(PF_UNIX, type, 0); if (fd < 0) { @@ -96,6 +101,9 @@ int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid) return -1; } + if (socketcon) + setsockcreatecon(NULL); + memset(&addr, 0 , sizeof(addr)); addr.sun_family = AF_UNIX; snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s", @@ -107,11 +115,11 @@ int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid) goto out_close; } - secon = NULL; + filecon = NULL; if (sehandle) { - ret = selabel_lookup(sehandle, &secon, addr.sun_path, S_IFSOCK); + ret = selabel_lookup(sehandle, &filecon, addr.sun_path, S_IFSOCK); if (ret == 0) - setfscreatecon(secon); + setfscreatecon(filecon); } ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr)); @@ -121,7 +129,7 @@ int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid) } setfscreatecon(NULL); - freecon(secon); + freecon(filecon); chown(addr.sun_path, uid, gid); chmod(addr.sun_path, perm); @@ -398,7 +406,9 @@ void open_devnull_stdio(void) void get_hardware_name(char *hardware, unsigned int *revision) { - char data[1024]; + const char *cpuinfo = "/proc/cpuinfo"; + char *data = NULL; + size_t len = 0, limit = 1024; int fd, n; char *x, *hw, *rev; @@ -406,14 +416,32 @@ void get_hardware_name(char *hardware, unsigned int *revision) if (hardware[0]) return; - fd = open("/proc/cpuinfo", O_RDONLY); + fd = open(cpuinfo, O_RDONLY); if (fd < 0) return; - n = read(fd, data, 1023); - close(fd); - if (n < 0) return; + for (;;) { + x = realloc(data, limit); + if (!x) { + ERROR("Failed to allocate memory to read %s\n", cpuinfo); + goto done; + } + data = x; + + n = read(fd, data + len, limit - len); + if (n < 0) { + ERROR("Failed reading %s: %s (%d)\n", cpuinfo, strerror(errno), errno); + goto done; + } + len += n; - data[n] = 0; + if (len < limit) + break; + + /* We filled the buffer, so increase size and loop to read more */ + limit *= 2; + } + + data[len] = 0; hw = strstr(data, "\nHardware"); rev = strstr(data, "\nRevision"); @@ -438,18 +466,22 @@ void get_hardware_name(char *hardware, unsigned int *revision) *revision = strtoul(x + 2, 0, 16); } } + +done: + close(fd); + free(data); } void import_kernel_cmdline(int in_qemu, void (*import_kernel_nv)(char *name, int in_qemu)) { - char cmdline[1024]; + char cmdline[2048]; char *ptr; int fd; fd = open("/proc/cmdline", O_RDONLY); if (fd >= 0) { - int n = read(fd, cmdline, 1023); + int n = read(fd, cmdline, sizeof(cmdline) - 1); if (n < 0) n = 0; /* get rid of trailing newline, it happens */ @@ -493,37 +525,12 @@ int make_dir(const char *path, mode_t mode) return rc; } -int restorecon(const char *pathname) +int restorecon(const char* pathname) { - char *secontext = NULL; - struct stat sb; - int i; - - if (is_selinux_enabled() <= 0 || !sehandle) - return 0; - - if (lstat(pathname, &sb) < 0) - return -errno; - if (selabel_lookup(sehandle, &secontext, pathname, sb.st_mode) < 0) - return -errno; - if (lsetfilecon(pathname, secontext) < 0) { - freecon(secontext); - return -errno; - } - freecon(secontext); - return 0; -} - -static int nftw_restorecon(const char* filename, const struct stat* statptr, - int fileflags, struct FTW* pftw) -{ - restorecon(filename); - return 0; + return selinux_android_restorecon(pathname); } int restorecon_recursive(const char* pathname) { - int fd_limit = 20; - int flags = FTW_DEPTH | FTW_MOUNT | FTW_PHYS; - return nftw(pathname, nftw_restorecon, fd_limit, flags); + return selinux_android_restorecon_recursive(pathname); } diff --git a/init/util.h b/init/util.h index 6bca4e6..04b8129 100644 --- a/init/util.h +++ b/init/util.h @@ -26,7 +26,7 @@ static const char *coldboot_done = "/dev/.coldboot_done"; int mtd_name_to_number(const char *name); int create_socket(const char *name, int type, mode_t perm, - uid_t uid, gid_t gid); + uid_t uid, gid_t gid, const char *socketcon); void *read_file(const char *fn, unsigned *_sz); time_t gettime(void); unsigned int decode_uid(const char *s); diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk new file mode 100644 index 0000000..80cd861 --- /dev/null +++ b/libbacktrace/Android.mk @@ -0,0 +1,290 @@ +LOCAL_PATH:= $(call my-dir) + +common_src := \ + BacktraceImpl.cpp \ + BacktraceMap.cpp \ + BacktraceThread.cpp \ + thread_utils.c \ + +common_cflags := \ + -Wall \ + -Wno-unused-parameter \ + -Werror \ + +common_conlyflags := \ + -std=gnu99 \ + +common_cppflags := \ + -std=gnu++11 \ + +common_shared_libs := \ + libcutils \ + libgccdemangle \ + liblog \ + +# To enable using libunwind on each arch, add it to this list. +libunwind_architectures := + +ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),$(libunwind_architectures))) + +#---------------------------------------------------------------------------- +# The native libbacktrace library with libunwind. +#---------------------------------------------------------------------------- +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + $(common_src) \ + UnwindCurrent.cpp \ + UnwindMap.cpp \ + UnwindPtrace.cpp \ + +LOCAL_CFLAGS := \ + $(common_cflags) \ + +LOCAL_CONLYFLAGS += \ + $(common_conlyflags) \ + +LOCAL_CPPFLAGS += \ + $(common_cppflags) \ + +LOCAL_MODULE := libbacktrace +LOCAL_MODULE_TAGS := optional + +LOCAL_C_INCLUDES := \ + $(common_c_includes) \ + external/libunwind/include \ + +LOCAL_SHARED_LIBRARIES := \ + $(common_shared_libs) \ + libunwind \ + libunwind-ptrace \ + +LOCAL_ADDITIONAL_DEPENDENCIES := \ + $(LOCAL_PATH)/Android.mk + +include external/stlport/libstlport.mk + +include $(BUILD_SHARED_LIBRARY) + +else + +#---------------------------------------------------------------------------- +# The native libbacktrace library with libcorkscrew. +#---------------------------------------------------------------------------- +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + $(common_src) \ + Corkscrew.cpp \ + +LOCAL_CFLAGS := \ + $(common_cflags) \ + +LOCAL_CONLYFLAGS += \ + $(common_conlyflags) \ + +LOCAL_CPPFLAGS += \ + $(common_cppflags) \ + +LOCAL_MODULE := libbacktrace +LOCAL_MODULE_TAGS := optional + +LOCAL_C_INCLUDES := \ + $(common_c_includes) \ + system/core/libcorkscrew \ + +LOCAL_SHARED_LIBRARIES := \ + $(common_shared_libs) \ + libcorkscrew \ + libdl \ + +LOCAL_ADDITIONAL_DEPENDENCIES := \ + $(LOCAL_PATH)/Android.mk + +include external/stlport/libstlport.mk + +include $(BUILD_SHARED_LIBRARY) + +endif + +#---------------------------------------------------------------------------- +# libbacktrace test library, all optimizations turned off +#---------------------------------------------------------------------------- +include $(CLEAR_VARS) + +LOCAL_MODULE := libbacktrace_test +LOCAL_MODULE_FLAGS := debug + +LOCAL_SRC_FILES := \ + backtrace_testlib.c + +LOCAL_CFLAGS += \ + -std=gnu99 \ + -O0 \ + +LOCAL_ADDITIONAL_DEPENDENCIES := \ + $(LOCAL_PATH)/Android.mk + +include $(BUILD_SHARED_LIBRARY) + +#---------------------------------------------------------------------------- +# libbacktrace test executable +#---------------------------------------------------------------------------- +include $(CLEAR_VARS) + +LOCAL_MODULE := backtrace_test +LOCAL_MODULE_FLAGS := debug + +LOCAL_SRC_FILES := \ + backtrace_test.cpp \ + thread_utils.c \ + +LOCAL_CFLAGS += \ + $(common_cflags) \ + -fno-builtin \ + -fstack-protector-all \ + -O0 \ + -g \ + -DGTEST_OS_LINUX_ANDROID \ + -DGTEST_HAS_STD_STRING \ + +ifeq ($(TARGET_ARCH),arm64) + $(info TODO: $(LOCAL_PATH)/Android.mk -fstack-protector not yet available for the AArch64 toolchain) + LOCAL_CFLAGS += -fno-stack-protector +endif # arm64 + +LOCAL_CONLYFLAGS += \ + $(common_conlyflags) \ + +LOCAL_CPPFLAGS += \ + $(common_cppflags) \ + +LOCAL_SHARED_LIBRARIES += \ + libcutils \ + libbacktrace_test \ + libbacktrace \ + +LOCAL_LDLIBS := \ + -lpthread \ + +LOCAL_ADDITIONAL_DEPENDENCIES := \ + $(LOCAL_PATH)/Android.mk + +include $(BUILD_NATIVE_TEST) + +#---------------------------------------------------------------------------- +# Only x86 host versions of libbacktrace supported. +#---------------------------------------------------------------------------- +ifeq ($(HOST_ARCH),x86) + +#---------------------------------------------------------------------------- +# The host libbacktrace library using libcorkscrew +#---------------------------------------------------------------------------- +include $(CLEAR_VARS) + + +LOCAL_CFLAGS += \ + $(common_cflags) \ + +LOCAL_CONLYFLAGS += \ + $(common_conlyflags) \ + +LOCAL_C_INCLUDES := \ + $(common_c_includes) \ + +LOCAL_SHARED_LIBRARIES := \ + libgccdemangle \ + liblog \ + +LOCAL_MODULE := libbacktrace +LOCAL_MODULE_TAGS := optional + +LOCAL_ADDITIONAL_DEPENDENCIES := \ + $(LOCAL_PATH)/Android.mk + +ifeq ($(HOST_OS),linux) +LOCAL_SRC_FILES += \ + $(common_src) \ + Corkscrew.cpp \ + +LOCAL_C_INCLUDES += \ + system/core/libcorkscrew \ + +LOCAL_SHARED_LIBRARIES := \ + libcorkscrew \ + +LOCAL_CPPFLAGS += \ + $(common_cppflags) \ + +LOCAL_LDLIBS += \ + -ldl \ + -lrt \ + +else +LOCAL_SRC_FILES += \ + BacktraceMap.cpp \ + +endif + +include $(BUILD_HOST_SHARED_LIBRARY) + +#---------------------------------------------------------------------------- +# The host test is only supported on linux. +#---------------------------------------------------------------------------- +ifeq ($(HOST_OS),linux) + +#---------------------------------------------------------------------------- +# libbacktrace host test library, all optimizations turned off +#---------------------------------------------------------------------------- +include $(CLEAR_VARS) + +LOCAL_MODULE := libbacktrace_test +LOCAL_MODULE_FLAGS := debug + +LOCAL_SRC_FILES := \ + backtrace_testlib.c + +LOCAL_CFLAGS += \ + -std=gnu99 \ + -O0 \ + +LOCAL_ADDITIONAL_DEPENDENCIES := \ + $(LOCAL_PATH)/Android.mk + +include $(BUILD_HOST_SHARED_LIBRARY) + +#---------------------------------------------------------------------------- +# libbacktrace host test executable +#---------------------------------------------------------------------------- +include $(CLEAR_VARS) + +LOCAL_MODULE := backtrace_test +LOCAL_MODULE_FLAGS := debug + +LOCAL_SRC_FILES := \ + backtrace_test.cpp \ + thread_utils.c \ + +LOCAL_CFLAGS += \ + $(common_cflags) \ + -fno-builtin \ + -fstack-protector-all \ + -O0 \ + -g \ + -DGTEST_HAS_STD_STRING \ + +LOCAL_SHARED_LIBRARIES := \ + libbacktrace_test \ + libbacktrace \ + +LOCAL_LDLIBS := \ + -lpthread \ + +LOCAL_ADDITIONAL_DEPENDENCIES := \ + $(LOCAL_PATH)/Android.mk + +include $(BUILD_HOST_NATIVE_TEST) + +endif # HOST_OS == linux + +endif # HOST_ARCH == x86 diff --git a/libbacktrace/BacktraceImpl.cpp b/libbacktrace/BacktraceImpl.cpp new file mode 100644 index 0000000..39296b4 --- /dev/null +++ b/libbacktrace/BacktraceImpl.cpp @@ -0,0 +1,208 @@ +/* + * 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. + */ + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ptrace.h> +#include <sys/types.h> +#include <unistd.h> + +#define __STDC_FORMAT_MACROS +#include <inttypes.h> + +#include <string> + +#include <backtrace/Backtrace.h> +#include <backtrace/BacktraceMap.h> + +#include "BacktraceImpl.h" +#include "thread_utils.h" + +//------------------------------------------------------------------------- +// Backtrace functions. +//------------------------------------------------------------------------- +Backtrace::Backtrace(BacktraceImpl* impl, pid_t pid, BacktraceMap* map) + : pid_(pid), tid_(-1), map_(map), map_shared_(true), impl_(impl) { + impl_->SetParent(this); + + if (map_ == NULL) { + map_ = BacktraceMap::Create(pid); + map_shared_ = false; + } +} + +Backtrace::~Backtrace() { + if (impl_) { + delete impl_; + impl_ = NULL; + } + + if (map_ && !map_shared_) { + delete map_; + map_ = NULL; + } +} + +bool Backtrace::Unwind(size_t num_ignore_frames) { + return impl_->Unwind(num_ignore_frames); +} + +extern "C" char* __cxa_demangle(const char* mangled, char* buf, size_t* len, + int* status); + +std::string Backtrace::GetFunctionName(uintptr_t pc, uintptr_t* offset) { + std::string func_name = impl_->GetFunctionNameRaw(pc, offset); + if (!func_name.empty()) { +#if defined(__APPLE__) + // Mac OS' __cxa_demangle demangles "f" as "float"; last tested on 10.7. + if (func_name[0] != '_') { + return func_name; + } +#endif + char* name = __cxa_demangle(func_name.c_str(), 0, 0, 0); + if (name) { + func_name = name; + free(name); + } + } + return func_name; +} + +bool Backtrace::VerifyReadWordArgs(uintptr_t ptr, uint32_t* out_value) { + if (ptr & 3) { + BACK_LOGW("invalid pointer %p", (void*)ptr); + *out_value = (uint32_t)-1; + return false; + } + return true; +} + +std::string Backtrace::FormatFrameData(size_t frame_num) { + if (frame_num >= frames_.size()) { + return ""; + } + return FormatFrameData(&frames_[frame_num]); +} + +std::string Backtrace::FormatFrameData(const backtrace_frame_data_t* frame) { + const char* map_name; + if (frame->map && !frame->map->name.empty()) { + map_name = frame->map->name.c_str(); + } else { + map_name = "<unknown>"; + } + + uintptr_t relative_pc; + if (frame->map) { + relative_pc = frame->pc - frame->map->start; + } else { + relative_pc = frame->pc; + } + + char buf[512]; + if (!frame->func_name.empty() && frame->func_offset) { + snprintf(buf, sizeof(buf), "#%02zu pc %0*" PRIxPTR " %s (%s+%" PRIuPTR ")", + frame->num, (int)sizeof(uintptr_t)*2, relative_pc, map_name, + frame->func_name.c_str(), frame->func_offset); + } else if (!frame->func_name.empty()) { + snprintf(buf, sizeof(buf), "#%02zu pc %0*" PRIxPTR " %s (%s)", frame->num, + (int)sizeof(uintptr_t)*2, relative_pc, map_name, frame->func_name.c_str()); + } else { + snprintf(buf, sizeof(buf), "#%02zu pc %0*" PRIxPTR " %s", frame->num, + (int)sizeof(uintptr_t)*2, relative_pc, map_name); + } + + return buf; +} + +const backtrace_map_t* Backtrace::FindMap(uintptr_t pc) { + return map_->Find(pc); +} + +//------------------------------------------------------------------------- +// BacktraceCurrent functions. +//------------------------------------------------------------------------- +BacktraceCurrent::BacktraceCurrent( + BacktraceImpl* impl, BacktraceMap* map) : Backtrace(impl, getpid(), map) { +} + +BacktraceCurrent::~BacktraceCurrent() { +} + +bool BacktraceCurrent::ReadWord(uintptr_t ptr, uint32_t* out_value) { + if (!VerifyReadWordArgs(ptr, out_value)) { + return false; + } + + const backtrace_map_t* map = FindMap(ptr); + if (map && map->flags & PROT_READ) { + *out_value = *reinterpret_cast<uint32_t*>(ptr); + return true; + } else { + BACK_LOGW("pointer %p not in a readable map", reinterpret_cast<void*>(ptr)); + *out_value = static_cast<uint32_t>(-1); + return false; + } +} + +//------------------------------------------------------------------------- +// BacktracePtrace functions. +//------------------------------------------------------------------------- +BacktracePtrace::BacktracePtrace( + BacktraceImpl* impl, pid_t pid, pid_t tid, BacktraceMap* map) + : Backtrace(impl, pid, map) { + tid_ = tid; +} + +BacktracePtrace::~BacktracePtrace() { +} + +bool BacktracePtrace::ReadWord(uintptr_t ptr, uint32_t* out_value) { + if (!VerifyReadWordArgs(ptr, out_value)) { + return false; + } + +#if defined(__APPLE__) + BACK_LOGW("MacOS does not support reading from another pid."); + return false; +#else + // ptrace() returns -1 and sets errno when the operation fails. + // To disambiguate -1 from a valid result, we clear errno beforehand. + errno = 0; + *out_value = ptrace(PTRACE_PEEKTEXT, Tid(), reinterpret_cast<void*>(ptr), NULL); + if (*out_value == static_cast<uint32_t>(-1) && errno) { + BACK_LOGW("invalid pointer %p reading from tid %d, ptrace() strerror(errno)=%s", + reinterpret_cast<void*>(ptr), Tid(), strerror(errno)); + return false; + } + return true; +#endif +} + +Backtrace* Backtrace::Create(pid_t pid, pid_t tid, BacktraceMap* map) { + if (pid == BACKTRACE_CURRENT_PROCESS || pid == getpid()) { + if (tid == BACKTRACE_CURRENT_THREAD || tid == gettid()) { + return CreateCurrentObj(map); + } else { + return CreateThreadObj(tid, map); + } + } else if (tid == BACKTRACE_CURRENT_THREAD) { + return CreatePtraceObj(pid, pid, map); + } else { + return CreatePtraceObj(pid, tid, map); + } +} diff --git a/libbacktrace/BacktraceImpl.h b/libbacktrace/BacktraceImpl.h new file mode 100644 index 0000000..af014d5 --- /dev/null +++ b/libbacktrace/BacktraceImpl.h @@ -0,0 +1,79 @@ +/* + * 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. + */ + +#ifndef _LIBBACKTRACE_BACKTRACE_IMPL_H +#define _LIBBACKTRACE_BACKTRACE_IMPL_H + +#include <backtrace/Backtrace.h> +#include <backtrace/BacktraceMap.h> + +#include <sys/types.h> +#include <log/log.h> + +// Macro to log the function name along with the warning message. +#define BACK_LOGW(format, ...) \ + ALOGW("%s: " format, __PRETTY_FUNCTION__, ##__VA_ARGS__) + +class BacktraceImpl { +public: + virtual ~BacktraceImpl() { } + + virtual bool Unwind(size_t num_ignore_frames) = 0; + + // The name returned is not demangled, Backtrace::GetFunctionName() + // takes care of demangling the name. + virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) = 0; + + void SetParent(Backtrace* backtrace) { backtrace_obj_ = backtrace; } + + inline pid_t Pid() { return backtrace_obj_->Pid(); } + inline pid_t Tid() { return backtrace_obj_->Tid(); } + + inline const backtrace_map_t* FindMap(uintptr_t addr) { + return backtrace_obj_->FindMap(addr); + } + inline std::string GetFunctionName(uintptr_t pc, uintptr_t* offset) { + return backtrace_obj_->GetFunctionName(pc, offset); + } + inline BacktraceMap* GetMap() { return backtrace_obj_->GetMap(); } + +protected: + inline std::vector<backtrace_frame_data_t>* GetFrames() { return &backtrace_obj_->frames_; } + + Backtrace* backtrace_obj_; +}; + +class BacktraceCurrent : public Backtrace { +public: + BacktraceCurrent(BacktraceImpl* impl, BacktraceMap* map); + virtual ~BacktraceCurrent(); + + bool ReadWord(uintptr_t ptr, uint32_t* out_value); +}; + +class BacktracePtrace : public Backtrace { +public: + BacktracePtrace(BacktraceImpl* impl, pid_t pid, pid_t tid, BacktraceMap* map); + virtual ~BacktracePtrace(); + + bool ReadWord(uintptr_t ptr, uint32_t* out_value); +}; + +Backtrace* CreateCurrentObj(BacktraceMap* map); +Backtrace* CreatePtraceObj(pid_t pid, pid_t tid, BacktraceMap* map); +Backtrace* CreateThreadObj(pid_t tid, BacktraceMap* map); + +#endif // _LIBBACKTRACE_BACKTRACE_IMPL_H diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp new file mode 100644 index 0000000..6320800 --- /dev/null +++ b/libbacktrace/BacktraceMap.cpp @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2014 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 <ctype.h> +#include <sys/types.h> +#include <unistd.h> + +#include <string> +#include <vector> + +#include <backtrace/backtrace_constants.h> +#include <backtrace/BacktraceMap.h> +#include <log/log.h> + +#include "thread_utils.h" +#include "BacktraceImpl.h" + +BacktraceMap::BacktraceMap(pid_t pid) : pid_(pid) { + if (pid_ < 0) { + pid_ = getpid(); + } +} + +BacktraceMap::~BacktraceMap() { +} + +const backtrace_map_t* BacktraceMap::Find(uintptr_t addr) { + for (BacktraceMap::const_iterator it = begin(); + it != end(); ++it) { + if (addr >= it->start && addr < it->end) { + return &*it; + } + } + return NULL; +} + +bool BacktraceMap::ParseLine(const char* line, backtrace_map_t* map) { + unsigned long int start; + unsigned long int end; + char permissions[5]; + int name_pos; + +#if defined(__APPLE__) +// Mac OS vmmap(1) output: +// __TEXT 0009f000-000a1000 [ 8K 8K] r-x/rwx SM=COW /Volumes/android/dalvik-dev/out/host/darwin-x86/bin/libcorkscrew_test\n +// 012345678901234567890123456789012345678901234567890123456789 +// 0 1 2 3 4 5 + if (sscanf(line, "%*21c %lx-%lx [%*13c] %3c/%*3c SM=%*3c %n", + &start, &end, permissions, &name_pos) != 3) { +#else +// Linux /proc/<pid>/maps lines: +// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so\n +// 012345678901234567890123456789012345678901234567890123456789 +// 0 1 2 3 4 5 + if (sscanf(line, "%lx-%lx %4s %*x %*x:%*x %*d%n", + &start, &end, permissions, &name_pos) != 3) { +#endif + return false; + } + + map->start = start; + map->end = end; + map->flags = PROT_NONE; + if (permissions[0] == 'r') { + map->flags |= PROT_READ; + } + if (permissions[1] == 'w') { + map->flags |= PROT_WRITE; + } + if (permissions[2] == 'x') { + map->flags |= PROT_EXEC; + } + + while (isspace(line[name_pos])) { + name_pos += 1; + } + map->name = line+name_pos; + if (!map->name.empty() && map->name[map->name.length()-1] == '\n') { + map->name.erase(map->name.length()-1); + } + + ALOGV("Parsed map: start=%p, end=%p, flags=%x, name=%s", + map->start, map->end, map->flags, map->name.c_str()); + return true; +} + +bool BacktraceMap::Build() { +#if defined(__APPLE__) + char cmd[sizeof(pid_t)*3 + sizeof("vmmap -w -resident -submap -allSplitLibs -interleaved ") + 1]; +#else + char path[sizeof(pid_t)*3 + sizeof("/proc//maps") + 1]; +#endif + char line[1024]; + +#if defined(__APPLE__) + // cmd is guaranteed to always be big enough to hold this string. + sprintf(cmd, "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid_); + FILE* fp = popen(cmd, "r"); +#else + // path is guaranteed to always be big enough to hold this string. + sprintf(path, "/proc/%d/maps", pid_); + FILE* fp = fopen(path, "r"); +#endif + if (fp == NULL) { + return false; + } + + while(fgets(line, sizeof(line), fp)) { + backtrace_map_t map; + if (ParseLine(line, &map)) { + maps_.push_back(map); + } + } +#if defined(__APPLE__) + pclose(fp); +#else + fclose(fp); +#endif + + return true; +} + +#if defined(__APPLE__) +// Corkscrew and libunwind don't compile on the mac, so create a generic +// map object. +BacktraceMap* BacktraceMap::Create(pid_t pid) { + BacktraceMap* map = new BacktraceMap(pid); + if (!map->Build()) { + delete map; + return NULL; + } + return map; +} +#endif diff --git a/libbacktrace/BacktraceThread.cpp b/libbacktrace/BacktraceThread.cpp new file mode 100644 index 0000000..5ffe516 --- /dev/null +++ b/libbacktrace/BacktraceThread.cpp @@ -0,0 +1,215 @@ +/* + * 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. + */ + +#include <errno.h> +#include <inttypes.h> +#include <pthread.h> +#include <signal.h> +#include <string.h> +#include <sys/types.h> + +#include <cutils/atomic.h> + +#include "BacktraceThread.h" +#include "thread_utils.h" + +//------------------------------------------------------------------------- +// ThreadEntry implementation. +//------------------------------------------------------------------------- +static ThreadEntry* g_list = NULL; +static pthread_mutex_t g_entry_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t g_sigaction_mutex = PTHREAD_MUTEX_INITIALIZER; + +ThreadEntry::ThreadEntry( + BacktraceThreadInterface* intf, pid_t pid, pid_t tid, size_t num_ignore_frames) + : thread_intf(intf), pid(pid), tid(tid), next(NULL), prev(NULL), + state(STATE_WAITING), num_ignore_frames(num_ignore_frames) { +} + +ThreadEntry::~ThreadEntry() { + pthread_mutex_lock(&g_entry_mutex); + if (g_list == this) { + g_list = next; + } else { + if (next) { + next->prev = prev; + } + prev->next = next; + } + pthread_mutex_unlock(&g_entry_mutex); + + next = NULL; + prev = NULL; +} + +ThreadEntry* ThreadEntry::AddThreadToUnwind( + BacktraceThreadInterface* intf, pid_t pid, pid_t tid, size_t num_ignore_frames) { + ThreadEntry* entry = new ThreadEntry(intf, pid, tid, num_ignore_frames); + + pthread_mutex_lock(&g_entry_mutex); + ThreadEntry* cur_entry = g_list; + while (cur_entry != NULL) { + if (cur_entry->Match(pid, tid)) { + // There is already an entry for this pid/tid, this is bad. + BACK_LOGW("Entry for pid %d tid %d already exists.", pid, tid); + + pthread_mutex_unlock(&g_entry_mutex); + return NULL; + } + cur_entry = cur_entry->next; + } + + // Add the entry to the list. + entry->next = g_list; + if (g_list) { + g_list->prev = entry; + } + g_list = entry; + pthread_mutex_unlock(&g_entry_mutex); + + return entry; +} + +//------------------------------------------------------------------------- +// BacktraceThread functions. +//------------------------------------------------------------------------- +static void SignalHandler(int n __attribute__((unused)), siginfo_t* siginfo, + void* sigcontext) { + if (pthread_mutex_lock(&g_entry_mutex) == 0) { + pid_t pid = getpid(); + pid_t tid = gettid(); + ThreadEntry* cur_entry = g_list; + while (cur_entry) { + if (cur_entry->Match(pid, tid)) { + break; + } + cur_entry = cur_entry->next; + } + pthread_mutex_unlock(&g_entry_mutex); + if (!cur_entry) { + BACK_LOGW("Unable to find pid %d tid %d information", pid, tid); + return; + } + + if (android_atomic_acquire_cas(STATE_WAITING, STATE_DUMPING, &cur_entry->state) == 0) { + cur_entry->thread_intf->ThreadUnwind(siginfo, sigcontext, + cur_entry->num_ignore_frames); + } + android_atomic_release_store(STATE_DONE, &cur_entry->state); + } +} + +BacktraceThread::BacktraceThread( + BacktraceImpl* impl, BacktraceThreadInterface* thread_intf, pid_t tid, + BacktraceMap* map) + : BacktraceCurrent(impl, map), thread_intf_(thread_intf) { + tid_ = tid; +} + +BacktraceThread::~BacktraceThread() { +} + +void BacktraceThread::FinishUnwind() { + for (std::vector<backtrace_frame_data_t>::iterator it = frames_.begin(); + it != frames_.end(); ++it) { + it->map = FindMap(it->pc); + + it->func_offset = 0; + it->func_name = GetFunctionName(it->pc, &it->func_offset); + } +} + +bool BacktraceThread::TriggerUnwindOnThread(ThreadEntry* entry) { + entry->state = STATE_WAITING; + + if (tgkill(Pid(), Tid(), SIGURG) != 0) { + BACK_LOGW("tgkill failed %s", strerror(errno)); + return false; + } + + // Allow up to ten seconds for the dump to start. + int wait_millis = 10000; + int32_t state; + while (true) { + state = android_atomic_acquire_load(&entry->state); + if (state != STATE_WAITING) { + break; + } + if (wait_millis--) { + usleep(1000); + } else { + break; + } + } + + bool cancelled = false; + if (state == STATE_WAITING) { + if (android_atomic_acquire_cas(state, STATE_CANCEL, &entry->state) == 0) { + BACK_LOGW("Cancelled dump of thread %d", entry->tid); + state = STATE_CANCEL; + cancelled = true; + } else { + state = android_atomic_acquire_load(&entry->state); + } + } + + // Wait for at most ten seconds for the cancel or dump to finish. + wait_millis = 10000; + while (android_atomic_acquire_load(&entry->state) != STATE_DONE) { + if (wait_millis--) { + usleep(1000); + } else { + BACK_LOGW("Didn't finish thread unwind in 60 seconds."); + break; + } + } + return !cancelled; +} + +bool BacktraceThread::Unwind(size_t num_ignore_frames) { + ThreadEntry* entry = ThreadEntry::AddThreadToUnwind( + thread_intf_, Pid(), Tid(), num_ignore_frames); + if (!entry) { + return false; + } + + // Prevent multiple threads trying to set the trigger action on different + // threads at the same time. + bool retval = false; + if (pthread_mutex_lock(&g_sigaction_mutex) == 0) { + struct sigaction act, oldact; + memset(&act, 0, sizeof(act)); + act.sa_sigaction = SignalHandler; + act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK; + sigemptyset(&act.sa_mask); + if (sigaction(SIGURG, &act, &oldact) == 0) { + retval = TriggerUnwindOnThread(entry); + sigaction(SIGURG, &oldact, NULL); + } else { + BACK_LOGW("sigaction failed %s", strerror(errno)); + } + pthread_mutex_unlock(&g_sigaction_mutex); + } else { + BACK_LOGW("unable to acquire sigaction mutex."); + } + + if (retval) { + FinishUnwind(); + } + delete entry; + + return retval; +} diff --git a/libbacktrace/BacktraceThread.h b/libbacktrace/BacktraceThread.h new file mode 100644 index 0000000..3412d58 --- /dev/null +++ b/libbacktrace/BacktraceThread.h @@ -0,0 +1,90 @@ +/* + * 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. + */ + +#ifndef _LIBBACKTRACE_BACKTRACE_THREAD_H +#define _LIBBACKTRACE_BACKTRACE_THREAD_H + +#include <inttypes.h> +#include <sys/types.h> + +#include "BacktraceImpl.h" + +enum state_e { + STATE_WAITING = 0, + STATE_DUMPING, + STATE_DONE, + STATE_CANCEL, +}; + +class BacktraceThreadInterface; + +struct ThreadEntry { + ThreadEntry( + BacktraceThreadInterface* impl, pid_t pid, pid_t tid, + size_t num_ignore_frames); + ~ThreadEntry(); + + bool Match(pid_t chk_pid, pid_t chk_tid) { return (chk_pid == pid && chk_tid == tid); } + + static ThreadEntry* AddThreadToUnwind( + BacktraceThreadInterface* thread_intf, pid_t pid, pid_t tid, + size_t num_ignored_frames); + + BacktraceThreadInterface* thread_intf; + pid_t pid; + pid_t tid; + ThreadEntry* next; + ThreadEntry* prev; + int32_t state; + int num_ignore_frames; +}; + +// Interface class that does not contain any local storage, only defines +// virtual functions to be defined by subclasses. +class BacktraceThreadInterface { +public: + virtual ~BacktraceThreadInterface() { } + + virtual void ThreadUnwind( + siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) = 0; +}; + +class BacktraceThread : public BacktraceCurrent { +public: + // impl and thread_intf should point to the same object, this allows + // the compiler to catch if an implementation does not properly + // subclass both. + BacktraceThread( + BacktraceImpl* impl, BacktraceThreadInterface* thread_intf, pid_t tid, + BacktraceMap* map); + virtual ~BacktraceThread(); + + virtual bool Unwind(size_t num_ignore_frames); + + virtual void ThreadUnwind( + siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) { + thread_intf_->ThreadUnwind(siginfo, sigcontext, num_ignore_frames); + } + +private: + virtual bool TriggerUnwindOnThread(ThreadEntry* entry); + + virtual void FinishUnwind(); + + BacktraceThreadInterface* thread_intf_; +}; + +#endif // _LIBBACKTRACE_BACKTRACE_THREAD_H diff --git a/libbacktrace/Corkscrew.cpp b/libbacktrace/Corkscrew.cpp new file mode 100644 index 0000000..efeee2e --- /dev/null +++ b/libbacktrace/Corkscrew.cpp @@ -0,0 +1,251 @@ +/* + * 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. + */ + +#define LOG_TAG "libbacktrace" + +#include <backtrace/Backtrace.h> + +#include <string.h> + +#include <backtrace-arch.h> +#include <corkscrew/backtrace.h> + +#ifndef __USE_GNU +#define __USE_GNU +#endif +#include <dlfcn.h> + +#include "Corkscrew.h" + +//------------------------------------------------------------------------- +// CorkscrewMap functions. +//------------------------------------------------------------------------- +CorkscrewMap::CorkscrewMap(pid_t pid) : BacktraceMap(pid), map_info_(NULL) { +} + +CorkscrewMap::~CorkscrewMap() { + if (map_info_) { + free_map_info_list(map_info_); + map_info_ = NULL; + } +} + +bool CorkscrewMap::Build() { + map_info_ = load_map_info_list(pid_); + + // Use the information in map_info_ to construct the BacktraceMap data + // rather than reparsing /proc/self/maps. + map_info_t* cur_map = map_info_; + while (cur_map) { + backtrace_map_t map; + map.start = cur_map->start; + map.end = cur_map->end; + map.flags = 0; + if (cur_map->is_readable) { + map.flags |= PROT_READ; + } + if (cur_map->is_writable) { + map.flags |= PROT_WRITE; + } + if (cur_map->is_executable) { + map.flags |= PROT_EXEC; + } + map.name = cur_map->name; + + // The maps are in descending order, but we want them in ascending order. + maps_.push_front(map); + + cur_map = cur_map->next; + } + return map_info_ != NULL; +} + +//------------------------------------------------------------------------- +// CorkscrewCommon functions. +//------------------------------------------------------------------------- +bool CorkscrewCommon::GenerateFrameData( + backtrace_frame_t* cork_frames, ssize_t num_frames) { + if (num_frames < 0) { + BACK_LOGW("libcorkscrew unwind failed."); + return false; + } + + std::vector<backtrace_frame_data_t>* frames = GetFrames(); + frames->resize(num_frames); + size_t i = 0; + for (std::vector<backtrace_frame_data_t>::iterator it = frames->begin(); + it != frames->end(); ++it, ++i) { + it->num = i; + it->pc = cork_frames[i].absolute_pc; + it->sp = cork_frames[i].stack_top; + it->stack_size = cork_frames[i].stack_size; + it->func_offset = 0; + + it->map = FindMap(it->pc); + it->func_name = GetFunctionName(it->pc, &it->func_offset); + } + return true; +} + +//------------------------------------------------------------------------- +// CorkscrewCurrent functions. +//------------------------------------------------------------------------- +CorkscrewCurrent::CorkscrewCurrent() { +} + +CorkscrewCurrent::~CorkscrewCurrent() { +} + +bool CorkscrewCurrent::Unwind(size_t num_ignore_frames) { + backtrace_frame_t frames[MAX_BACKTRACE_FRAMES]; + ssize_t num_frames = unwind_backtrace(frames, num_ignore_frames, MAX_BACKTRACE_FRAMES); + + return GenerateFrameData(frames, num_frames); +} + +std::string CorkscrewCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) { + *offset = 0; + + Dl_info info; + const backtrace_map_t* map = FindMap(pc); + if (map) { + if (dladdr((const void*)pc, &info)) { + if (info.dli_sname) { + *offset = pc - map->start - (uintptr_t)info.dli_saddr + (uintptr_t)info.dli_fbase; + return info.dli_sname; + } + } else { + // dladdr(3) didn't find a symbol; maybe it's static? Look in the ELF file... + symbol_table_t* symbol_table = load_symbol_table(map->name.c_str()); + if (symbol_table) { + // First check if we can find the symbol using a relative pc. + std::string name; + const symbol_t* elf_symbol = find_symbol(symbol_table, pc - map->start); + if (elf_symbol) { + name = elf_symbol->name; + *offset = pc - map->start - elf_symbol->start; + } else if ((elf_symbol = find_symbol(symbol_table, pc)) != NULL) { + // Found the symbol using the absolute pc. + name = elf_symbol->name; + *offset = pc - elf_symbol->start; + } + free_symbol_table(symbol_table); + return name; + } + } + } + return ""; +} + +//------------------------------------------------------------------------- +// CorkscrewThread functions. +//------------------------------------------------------------------------- +CorkscrewThread::CorkscrewThread() { +} + +CorkscrewThread::~CorkscrewThread() { +} + +void CorkscrewThread::ThreadUnwind( + siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) { + backtrace_frame_t cork_frames[MAX_BACKTRACE_FRAMES]; + CorkscrewMap* map = static_cast<CorkscrewMap*>(GetMap()); + ssize_t num_frames = unwind_backtrace_signal_arch( + siginfo, sigcontext, map->GetMapInfo(), cork_frames, + num_ignore_frames, MAX_BACKTRACE_FRAMES); + if (num_frames > 0) { + std::vector<backtrace_frame_data_t>* frames = GetFrames(); + frames->resize(num_frames); + size_t i = 0; + for (std::vector<backtrace_frame_data_t>::iterator it = frames->begin(); + it != frames->end(); ++it, ++i) { + it->num = i; + it->pc = cork_frames[i].absolute_pc; + it->sp = cork_frames[i].stack_top; + it->stack_size = cork_frames[i].stack_size; + it->map = NULL; + it->func_offset = 0; + } + } +} + +//------------------------------------------------------------------------- +// CorkscrewPtrace functions. +//------------------------------------------------------------------------- +CorkscrewPtrace::CorkscrewPtrace() : ptrace_context_(NULL) { +} + +CorkscrewPtrace::~CorkscrewPtrace() { + if (ptrace_context_) { + free_ptrace_context(ptrace_context_); + ptrace_context_ = NULL; + } +} + +bool CorkscrewPtrace::Unwind(size_t num_ignore_frames) { + ptrace_context_ = load_ptrace_context(Tid()); + + backtrace_frame_t frames[MAX_BACKTRACE_FRAMES]; + ssize_t num_frames = unwind_backtrace_ptrace( + Tid(), ptrace_context_, frames, num_ignore_frames, MAX_BACKTRACE_FRAMES); + + return GenerateFrameData(frames, num_frames); +} + +std::string CorkscrewPtrace::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) { + // Get information about a different process. + const map_info_t* map_info; + const symbol_t* symbol; + find_symbol_ptrace(ptrace_context_, pc, &map_info, &symbol); + char* symbol_name = NULL; + if (symbol) { + if (map_info) { + *offset = pc - map_info->start - symbol->start; + } + symbol_name = symbol->name; + return symbol_name; + } + + return ""; +} + +//------------------------------------------------------------------------- +// C++ object creation functions. +//------------------------------------------------------------------------- +Backtrace* CreateCurrentObj(BacktraceMap* map) { + return new BacktraceCurrent(new CorkscrewCurrent(), map); +} + +Backtrace* CreatePtraceObj(pid_t pid, pid_t tid, BacktraceMap* map) { + return new BacktracePtrace(new CorkscrewPtrace(), pid, tid, map); +} + +Backtrace* CreateThreadObj(pid_t tid, BacktraceMap* map) { + CorkscrewThread* thread_obj = new CorkscrewThread(); + return new BacktraceThread(thread_obj, thread_obj, tid, map); +} + +//------------------------------------------------------------------------- +// BacktraceMap create function. +//------------------------------------------------------------------------- +BacktraceMap* BacktraceMap::Create(pid_t pid) { + BacktraceMap* map = new CorkscrewMap(pid); + if (!map->Build()) { + delete map; + return NULL; + } + return map; +} diff --git a/libbacktrace/Corkscrew.h b/libbacktrace/Corkscrew.h new file mode 100644 index 0000000..1633398 --- /dev/null +++ b/libbacktrace/Corkscrew.h @@ -0,0 +1,82 @@ +/* + * 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. + */ + +#ifndef _LIBBACKTRACE_CORKSCREW_H +#define _LIBBACKTRACE_CORKSCREW_H + +#include <inttypes.h> + +#include <string> + +#include <backtrace/Backtrace.h> +#include <backtrace/BacktraceMap.h> + +#include <corkscrew/backtrace.h> + +#include "BacktraceImpl.h" +#include "BacktraceThread.h" + +class CorkscrewMap : public BacktraceMap { +public: + CorkscrewMap(pid_t pid); + virtual ~CorkscrewMap(); + + virtual bool Build(); + + map_info_t* GetMapInfo() { return map_info_; } + +private: + map_info_t* map_info_; +}; + +class CorkscrewCommon : public BacktraceImpl { +public: + bool GenerateFrameData(backtrace_frame_t* cork_frames, ssize_t num_frames); +}; + +class CorkscrewCurrent : public CorkscrewCommon { +public: + CorkscrewCurrent(); + virtual ~CorkscrewCurrent(); + + virtual bool Unwind(size_t num_ignore_threads); + + virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset); +}; + +class CorkscrewThread : public CorkscrewCurrent, public BacktraceThreadInterface { +public: + CorkscrewThread(); + virtual ~CorkscrewThread(); + + virtual void ThreadUnwind( + siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames); +}; + +class CorkscrewPtrace : public CorkscrewCommon { +public: + CorkscrewPtrace(); + virtual ~CorkscrewPtrace(); + + virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset); + + virtual bool Unwind(size_t num_ignore_threads); + +private: + ptrace_context_t* ptrace_context_; +}; + +#endif // _LIBBACKTRACE_CORKSCREW_H diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp new file mode 100644 index 0000000..17b71b9 --- /dev/null +++ b/libbacktrace/UnwindCurrent.cpp @@ -0,0 +1,191 @@ +/* + * 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. + */ + +#define LOG_TAG "libbacktrace" + +#include <sys/types.h> + +#include <backtrace/Backtrace.h> +#include <backtrace/BacktraceMap.h> + +#define UNW_LOCAL_ONLY +#include <libunwind.h> + +#include "UnwindCurrent.h" +#include "UnwindMap.h" + +// Define the ucontext_t structures needed for each supported arch. +#if defined(__arm__) + // The current version of the <signal.h> doesn't define ucontext_t. + #include <asm/sigcontext.h> // Ensure 'struct sigcontext' is defined. + + // Machine context at the time a signal was raised. + typedef struct ucontext { + uint32_t uc_flags; + struct ucontext* uc_link; + stack_t uc_stack; + struct sigcontext uc_mcontext; + uint32_t uc_sigmask; + } ucontext_t; +#elif defined(__i386__) + #include <asm/sigcontext.h> + #include <asm/ucontext.h> + typedef struct ucontext ucontext_t; +#elif !defined(__mips__) && !defined(__aarch64__) + #error Unsupported architecture. +#endif + +//------------------------------------------------------------------------- +// UnwindCurrent functions. +//------------------------------------------------------------------------- +UnwindCurrent::UnwindCurrent() { +} + +UnwindCurrent::~UnwindCurrent() { +} + +bool UnwindCurrent::Unwind(size_t num_ignore_frames) { + int ret = unw_getcontext(&context_); + if (ret < 0) { + BACK_LOGW("unw_getcontext failed %d", ret); + return false; + } + return UnwindFromContext(num_ignore_frames, true); +} + +std::string UnwindCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) { + *offset = 0; + char buf[512]; + unw_word_t value; + if (unw_get_proc_name_by_ip(unw_local_addr_space, pc, buf, sizeof(buf), + &value, &context_) >= 0 && buf[0] != '\0') { + *offset = static_cast<uintptr_t>(value); + return buf; + } + return ""; +} + +bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, bool resolve) { + // The cursor structure is pretty large, do not put it on the stack. + unw_cursor_t* cursor = new unw_cursor_t; + int ret = unw_init_local(cursor, &context_); + if (ret < 0) { + BACK_LOGW("unw_init_local failed %d", ret); + delete cursor; + return false; + } + + std::vector<backtrace_frame_data_t>* frames = GetFrames(); + frames->reserve(MAX_BACKTRACE_FRAMES); + size_t num_frames = 0; + do { + unw_word_t pc; + ret = unw_get_reg(cursor, UNW_REG_IP, &pc); + if (ret < 0) { + BACK_LOGW("Failed to read IP %d", ret); + break; + } + unw_word_t sp; + ret = unw_get_reg(cursor, UNW_REG_SP, &sp); + if (ret < 0) { + BACK_LOGW("Failed to read SP %d", ret); + break; + } + + if (num_ignore_frames == 0) { + frames->resize(num_frames+1); + backtrace_frame_data_t* frame = &frames->at(num_frames); + frame->num = num_frames; + frame->pc = static_cast<uintptr_t>(pc); + frame->sp = static_cast<uintptr_t>(sp); + frame->stack_size = 0; + + if (num_frames > 0) { + // Set the stack size for the previous frame. + backtrace_frame_data_t* prev = &frames->at(num_frames-1); + prev->stack_size = frame->sp - prev->sp; + } + + if (resolve) { + frame->func_name = GetFunctionName(frame->pc, &frame->func_offset); + frame->map = FindMap(frame->pc); + } else { + frame->map = NULL; + frame->func_offset = 0; + } + num_frames++; + } else { + num_ignore_frames--; + } + ret = unw_step (cursor); + } while (ret > 0 && num_frames < MAX_BACKTRACE_FRAMES); + + delete cursor; + return true; +} + +void UnwindCurrent::ExtractContext(void* sigcontext) { + unw_tdep_context_t* context = reinterpret_cast<unw_tdep_context_t*>(&context_); + const ucontext_t* uc = reinterpret_cast<const ucontext_t*>(sigcontext); + +#if defined(__arm__) + context->regs[0] = uc->uc_mcontext.arm_r0; + context->regs[1] = uc->uc_mcontext.arm_r1; + context->regs[2] = uc->uc_mcontext.arm_r2; + context->regs[3] = uc->uc_mcontext.arm_r3; + context->regs[4] = uc->uc_mcontext.arm_r4; + context->regs[5] = uc->uc_mcontext.arm_r5; + context->regs[6] = uc->uc_mcontext.arm_r6; + context->regs[7] = uc->uc_mcontext.arm_r7; + context->regs[8] = uc->uc_mcontext.arm_r8; + context->regs[9] = uc->uc_mcontext.arm_r9; + context->regs[10] = uc->uc_mcontext.arm_r10; + context->regs[11] = uc->uc_mcontext.arm_fp; + context->regs[12] = uc->uc_mcontext.arm_ip; + context->regs[13] = uc->uc_mcontext.arm_sp; + context->regs[14] = uc->uc_mcontext.arm_lr; + context->regs[15] = uc->uc_mcontext.arm_pc; +#elif defined(__mips__) || defined(__i386__) + context->uc_mcontext = uc->uc_mcontext; +#endif +} + +//------------------------------------------------------------------------- +// UnwindThread functions. +//------------------------------------------------------------------------- +UnwindThread::UnwindThread() { +} + +UnwindThread::~UnwindThread() { +} + +void UnwindThread::ThreadUnwind( + siginfo_t* /*siginfo*/, void* sigcontext, size_t num_ignore_frames) { + ExtractContext(sigcontext); + UnwindFromContext(num_ignore_frames, false); +} + +//------------------------------------------------------------------------- +// C++ object creation function. +//------------------------------------------------------------------------- +Backtrace* CreateCurrentObj(BacktraceMap* map) { + return new BacktraceCurrent(new UnwindCurrent(), map); +} + +Backtrace* CreateThreadObj(pid_t tid, BacktraceMap* map) { + UnwindThread* thread_obj = new UnwindThread(); + return new BacktraceThread(thread_obj, thread_obj, tid, map); +} diff --git a/libbacktrace/UnwindCurrent.h b/libbacktrace/UnwindCurrent.h new file mode 100644 index 0000000..acce110 --- /dev/null +++ b/libbacktrace/UnwindCurrent.h @@ -0,0 +1,54 @@ +/* + * 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. + */ + +#ifndef _LIBBACKTRACE_UNWIND_CURRENT_H +#define _LIBBACKTRACE_UNWIND_CURRENT_H + +#include <string> + +#include "BacktraceImpl.h" +#include "BacktraceThread.h" + +#define UNW_LOCAL_ONLY +#include <libunwind.h> + +class UnwindCurrent : public BacktraceImpl { +public: + UnwindCurrent(); + virtual ~UnwindCurrent(); + + virtual bool Unwind(size_t num_ignore_frames); + + virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset); + + bool UnwindFromContext(size_t num_ignore_frames, bool resolve); + + void ExtractContext(void* sigcontext); + +protected: + unw_context_t context_; +}; + +class UnwindThread : public UnwindCurrent, public BacktraceThreadInterface { +public: + UnwindThread(); + virtual ~UnwindThread(); + + virtual void ThreadUnwind( + siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames); +}; + +#endif // _LIBBACKTRACE_UNWIND_CURRENT_H diff --git a/libbacktrace/UnwindMap.cpp b/libbacktrace/UnwindMap.cpp new file mode 100644 index 0000000..03bb192 --- /dev/null +++ b/libbacktrace/UnwindMap.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2014 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 "libbacktrace" + +#include <pthread.h> +#include <sys/types.h> +#include <unistd.h> + +#include <backtrace/BacktraceMap.h> + +#include <libunwind.h> + +#include "UnwindMap.h" + +//------------------------------------------------------------------------- +// libunwind has a single shared address space for the current process +// aka local. If multiple maps are created for the current pid, then +// only update the local address space once, and keep a reference count +// of maps using the same map cursor. +//------------------------------------------------------------------------- +static pthread_mutex_t g_map_mutex = PTHREAD_MUTEX_INITIALIZER; +static unw_map_cursor_t g_map_cursor; +static int g_map_references = 0; + +UnwindMap::UnwindMap(pid_t pid) : BacktraceMap(pid) { + map_cursor_.map_list = NULL; +} + +UnwindMap::~UnwindMap() { + if (pid_ == getpid()) { + pthread_mutex_lock(&g_map_mutex); + if (--g_map_references == 0) { + // Clear the local address space map. + unw_map_set(unw_local_addr_space, NULL); + unw_map_cursor_destroy(&map_cursor_); + } + pthread_mutex_unlock(&g_map_mutex); + } else { + unw_map_cursor_destroy(&map_cursor_); + } +} + +bool UnwindMap::Build() { + bool return_value = true; + if (pid_ == getpid()) { + pthread_mutex_lock(&g_map_mutex); + if (g_map_references == 0) { + return_value = (unw_map_cursor_create(&map_cursor_, pid_) == 0); + if (return_value) { + // Set the local address space to this cursor map. + unw_map_set(unw_local_addr_space, &map_cursor_); + g_map_references = 1; + g_map_cursor = map_cursor_; + } + } else { + g_map_references++; + map_cursor_ = g_map_cursor; + } + pthread_mutex_unlock(&g_map_mutex); + } else { + return_value = (unw_map_cursor_create(&map_cursor_, pid_) == 0); + } + + if (!return_value) + return false; + + // Use the map_cursor information to construct the BacktraceMap data + // rather than reparsing /proc/self/maps. + unw_map_cursor_reset(&map_cursor_); + unw_map_t unw_map; + while (unw_map_cursor_get(&map_cursor_, &unw_map)) { + backtrace_map_t map; + + map.start = unw_map.start; + map.end = unw_map.end; + map.flags = unw_map.flags; + map.name = unw_map.path; + + // The maps are in descending order, but we want them in ascending order. + maps_.push_front(map); + } + + return true; +} + +//------------------------------------------------------------------------- +// BacktraceMap create function. +//------------------------------------------------------------------------- +BacktraceMap* BacktraceMap::Create(pid_t pid) { + BacktraceMap* map = new UnwindMap(pid); + if (!map->Build()) { + delete map; + return NULL; + } + return map; +} diff --git a/libbacktrace/UnwindMap.h b/libbacktrace/UnwindMap.h new file mode 100644 index 0000000..5a874e8 --- /dev/null +++ b/libbacktrace/UnwindMap.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2014 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 _LIBBACKTRACE_UNWIND_MAP_H +#define _LIBBACKTRACE_UNWIND_MAP_H + +#include <backtrace/BacktraceMap.h> + +// The unw_map_cursor_t structure is different depending on whether it is +// the local or remote version. In order to get the correct version, include +// libunwind.h first then this header. + +class UnwindMap : public BacktraceMap { +public: + UnwindMap(pid_t pid); + virtual ~UnwindMap(); + + virtual bool Build(); + + unw_map_cursor_t* GetMapCursor() { return &map_cursor_; } + +private: + unw_map_cursor_t map_cursor_; +}; + +#endif // _LIBBACKTRACE_UNWIND_MAP_H diff --git a/libbacktrace/UnwindPtrace.cpp b/libbacktrace/UnwindPtrace.cpp new file mode 100644 index 0000000..732dae8 --- /dev/null +++ b/libbacktrace/UnwindPtrace.cpp @@ -0,0 +1,133 @@ +/* + * 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. + */ + +#define LOG_TAG "libbacktrace" + +#include <backtrace/Backtrace.h> +#include <backtrace/BacktraceMap.h> + +#include <sys/types.h> +#include <string.h> + +#include <libunwind.h> +#include <libunwind-ptrace.h> + +#include "UnwindMap.h" +#include "UnwindPtrace.h" + +UnwindPtrace::UnwindPtrace() : addr_space_(NULL), upt_info_(NULL) { +} + +UnwindPtrace::~UnwindPtrace() { + if (upt_info_) { + _UPT_destroy(upt_info_); + upt_info_ = NULL; + } + if (addr_space_) { + // Remove the map from the address space before destroying it. + // It will be freed in the UnwindMap destructor. + unw_map_set(addr_space_, NULL); + + unw_destroy_addr_space(addr_space_); + addr_space_ = NULL; + } +} + +bool UnwindPtrace::Unwind(size_t num_ignore_frames) { + addr_space_ = unw_create_addr_space(&_UPT_accessors, 0); + if (!addr_space_) { + BACK_LOGW("unw_create_addr_space failed."); + return false; + } + + UnwindMap* map = static_cast<UnwindMap*>(GetMap()); + unw_map_set(addr_space_, map->GetMapCursor()); + + upt_info_ = reinterpret_cast<struct UPT_info*>(_UPT_create(Tid())); + if (!upt_info_) { + BACK_LOGW("Failed to create upt info."); + return false; + } + + unw_cursor_t cursor; + int ret = unw_init_remote(&cursor, addr_space_, upt_info_); + if (ret < 0) { + BACK_LOGW("unw_init_remote failed %d", ret); + return false; + } + + std::vector<backtrace_frame_data_t>* frames = GetFrames(); + frames->reserve(MAX_BACKTRACE_FRAMES); + size_t num_frames = 0; + do { + unw_word_t pc; + ret = unw_get_reg(&cursor, UNW_REG_IP, &pc); + if (ret < 0) { + BACK_LOGW("Failed to read IP %d", ret); + break; + } + unw_word_t sp; + ret = unw_get_reg(&cursor, UNW_REG_SP, &sp); + if (ret < 0) { + BACK_LOGW("Failed to read SP %d", ret); + break; + } + + if (num_ignore_frames == 0) { + frames->resize(num_frames+1); + backtrace_frame_data_t* frame = &frames->at(num_frames); + frame->num = num_frames; + frame->pc = static_cast<uintptr_t>(pc); + frame->sp = static_cast<uintptr_t>(sp); + frame->stack_size = 0; + + if (num_frames > 0) { + backtrace_frame_data_t* prev = &frames->at(num_frames-1); + prev->stack_size = frame->sp - prev->sp; + } + + frame->func_name = GetFunctionName(frame->pc, &frame->func_offset); + + frame->map = FindMap(frame->pc); + + num_frames++; + } else { + num_ignore_frames--; + } + ret = unw_step (&cursor); + } while (ret > 0 && num_frames < MAX_BACKTRACE_FRAMES); + + return true; +} + +std::string UnwindPtrace::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) { + *offset = 0; + char buf[512]; + unw_word_t value; + if (unw_get_proc_name_by_ip(addr_space_, pc, buf, sizeof(buf), &value, + upt_info_) >= 0 && buf[0] != '\0') { + *offset = static_cast<uintptr_t>(value); + return buf; + } + return ""; +} + +//------------------------------------------------------------------------- +// C++ object creation function. +//------------------------------------------------------------------------- +Backtrace* CreatePtraceObj(pid_t pid, pid_t tid, BacktraceMap* map) { + return new BacktracePtrace(new UnwindPtrace(), pid, tid, map); +} diff --git a/libbacktrace/UnwindPtrace.h b/libbacktrace/UnwindPtrace.h new file mode 100644 index 0000000..1e82117 --- /dev/null +++ b/libbacktrace/UnwindPtrace.h @@ -0,0 +1,40 @@ +/* + * 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. + */ + +#ifndef _LIBBACKTRACE_UNWIND_PTRACE_H +#define _LIBBACKTRACE_UNWIND_PTRACE_H + +#include <string> + +#include "BacktraceImpl.h" + +#include <libunwind.h> + +class UnwindPtrace : public BacktraceImpl { +public: + UnwindPtrace(); + virtual ~UnwindPtrace(); + + virtual bool Unwind(size_t num_ignore_frames); + + virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset); + +private: + unw_addr_space_t addr_space_; + struct UPT_info* upt_info_; +}; + +#endif // _LIBBACKTRACE_UNWIND_PTRACE_H diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp new file mode 100644 index 0000000..23eaf92 --- /dev/null +++ b/libbacktrace/backtrace_test.cpp @@ -0,0 +1,695 @@ +/* + * 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. + */ + +#include <dirent.h> +#include <errno.h> +#include <pthread.h> +#include <signal.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ptrace.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <time.h> +#include <unistd.h> + +#include <backtrace/Backtrace.h> +#include <backtrace/BacktraceMap.h> +#include <UniquePtr.h> + +#include <cutils/atomic.h> +#include <gtest/gtest.h> + +#include <vector> + +#include "thread_utils.h" + +// Number of microseconds per milliseconds. +#define US_PER_MSEC 1000 + +// Number of nanoseconds in a second. +#define NS_PER_SEC 1000000000ULL + +// Number of simultaneous dumping operations to perform. +#define NUM_THREADS 20 + +// Number of simultaneous threads running in our forked process. +#define NUM_PTRACE_THREADS 5 + +struct thread_t { + pid_t tid; + int32_t state; + pthread_t threadId; +}; + +struct dump_thread_t { + thread_t thread; + Backtrace* backtrace; + int32_t* now; + int32_t done; +}; + +extern "C" { +// Prototypes for functions in the test library. +int test_level_one(int, int, int, int, void (*)(void*), void*); + +int test_recursive_call(int, void (*)(void*), void*); +} + +uint64_t NanoTime() { + struct timespec t = { 0, 0 }; + clock_gettime(CLOCK_MONOTONIC, &t); + return static_cast<uint64_t>(t.tv_sec * NS_PER_SEC + t.tv_nsec); +} + +void DumpFrames(Backtrace* backtrace) { + if (backtrace->NumFrames() == 0) { + printf(" No frames to dump\n"); + return; + } + + for (size_t i = 0; i < backtrace->NumFrames(); i++) { + printf(" %s\n", backtrace->FormatFrameData(i).c_str()); + } +} + +void WaitForStop(pid_t pid) { + uint64_t start = NanoTime(); + + siginfo_t si; + while (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) < 0 && (errno == EINTR || errno == ESRCH)) { + if ((NanoTime() - start) > NS_PER_SEC) { + printf("The process did not get to a stopping point in 1 second.\n"); + break; + } + usleep(US_PER_MSEC); + } +} + +bool ReadyLevelBacktrace(Backtrace* backtrace) { + // See if test_level_four is in the backtrace. + bool found = false; + for (Backtrace::const_iterator it = backtrace->begin(); it != backtrace->end(); ++it) { + if (it->func_name == "test_level_four") { + found = true; + break; + } + } + + return found; +} + +void VerifyLevelDump(Backtrace* backtrace) { + ASSERT_GT(backtrace->NumFrames(), static_cast<size_t>(0)); + ASSERT_LT(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES)); + + // Look through the frames starting at the highest to find the + // frame we want. + size_t frame_num = 0; + for (size_t i = backtrace->NumFrames()-1; i > 2; i--) { + if (backtrace->GetFrame(i)->func_name == "test_level_one") { + frame_num = i; + break; + } + } + ASSERT_LT(static_cast<size_t>(0), frame_num); + ASSERT_LE(static_cast<size_t>(3), frame_num); + + ASSERT_EQ(backtrace->GetFrame(frame_num)->func_name, "test_level_one"); + ASSERT_EQ(backtrace->GetFrame(frame_num-1)->func_name, "test_level_two"); + ASSERT_EQ(backtrace->GetFrame(frame_num-2)->func_name, "test_level_three"); + ASSERT_EQ(backtrace->GetFrame(frame_num-3)->func_name, "test_level_four"); +} + +void VerifyLevelBacktrace(void*) { + UniquePtr<Backtrace> backtrace( + Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD)); + ASSERT_TRUE(backtrace.get() != NULL); + ASSERT_TRUE(backtrace->Unwind(0)); + + VerifyLevelDump(backtrace.get()); +} + +bool ReadyMaxBacktrace(Backtrace* backtrace) { + return (backtrace->NumFrames() == MAX_BACKTRACE_FRAMES); +} + +void VerifyMaxDump(Backtrace* backtrace) { + ASSERT_EQ(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES)); + // Verify that the last frame is our recursive call. + ASSERT_EQ(backtrace->GetFrame(MAX_BACKTRACE_FRAMES-1)->func_name, + "test_recursive_call"); +} + +void VerifyMaxBacktrace(void*) { + UniquePtr<Backtrace> backtrace( + Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD)); + ASSERT_TRUE(backtrace.get() != NULL); + ASSERT_TRUE(backtrace->Unwind(0)); + + VerifyMaxDump(backtrace.get()); +} + +void ThreadSetState(void* data) { + thread_t* thread = reinterpret_cast<thread_t*>(data); + android_atomic_acquire_store(1, &thread->state); + volatile int i = 0; + while (thread->state) { + i++; + } +} + +void VerifyThreadTest(pid_t tid, void (*VerifyFunc)(Backtrace*)) { + UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), tid)); + ASSERT_TRUE(backtrace.get() != NULL); + ASSERT_TRUE(backtrace->Unwind(0)); + + VerifyFunc(backtrace.get()); +} + +bool WaitForNonZero(int32_t* value, uint64_t seconds) { + uint64_t start = NanoTime(); + do { + if (android_atomic_acquire_load(value)) { + return true; + } + } while ((NanoTime() - start) < seconds * NS_PER_SEC); + return false; +} + +TEST(libbacktrace, local_trace) { + ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelBacktrace, NULL), 0); +} + +void VerifyIgnoreFrames( + Backtrace* bt_all, Backtrace* bt_ign1, + Backtrace* bt_ign2, const char* cur_proc) { + EXPECT_EQ(bt_all->NumFrames(), bt_ign1->NumFrames() + 1); + EXPECT_EQ(bt_all->NumFrames(), bt_ign2->NumFrames() + 2); + + // Check all of the frames are the same > the current frame. + bool check = (cur_proc == NULL); + for (size_t i = 0; i < bt_ign2->NumFrames(); i++) { + if (check) { + EXPECT_EQ(bt_ign2->GetFrame(i)->pc, bt_ign1->GetFrame(i+1)->pc); + EXPECT_EQ(bt_ign2->GetFrame(i)->sp, bt_ign1->GetFrame(i+1)->sp); + EXPECT_EQ(bt_ign2->GetFrame(i)->stack_size, bt_ign1->GetFrame(i+1)->stack_size); + + EXPECT_EQ(bt_ign2->GetFrame(i)->pc, bt_all->GetFrame(i+2)->pc); + EXPECT_EQ(bt_ign2->GetFrame(i)->sp, bt_all->GetFrame(i+2)->sp); + EXPECT_EQ(bt_ign2->GetFrame(i)->stack_size, bt_all->GetFrame(i+2)->stack_size); + } + if (!check && bt_ign2->GetFrame(i)->func_name == cur_proc) { + check = true; + } + } +} + +void VerifyLevelIgnoreFrames(void*) { + UniquePtr<Backtrace> all( + Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD)); + ASSERT_TRUE(all.get() != NULL); + ASSERT_TRUE(all->Unwind(0)); + + UniquePtr<Backtrace> ign1( + Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD)); + ASSERT_TRUE(ign1.get() != NULL); + ASSERT_TRUE(ign1->Unwind(1)); + + UniquePtr<Backtrace> ign2( + Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD)); + ASSERT_TRUE(ign2.get() != NULL); + ASSERT_TRUE(ign2->Unwind(2)); + + VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), "VerifyLevelIgnoreFrames"); +} + +TEST(libbacktrace, local_trace_ignore_frames) { + ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelIgnoreFrames, NULL), 0); +} + +TEST(libbacktrace, local_max_trace) { + ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxBacktrace, NULL), 0); +} + +void VerifyProcTest(pid_t pid, pid_t tid, bool share_map, + bool (*ReadyFunc)(Backtrace*), + void (*VerifyFunc)(Backtrace*)) { + pid_t ptrace_tid; + if (tid < 0) { + ptrace_tid = pid; + } else { + ptrace_tid = tid; + } + uint64_t start = NanoTime(); + bool verified = false; + do { + usleep(US_PER_MSEC); + if (ptrace(PTRACE_ATTACH, ptrace_tid, 0, 0) == 0) { + // Wait for the process to get to a stopping point. + WaitForStop(ptrace_tid); + + UniquePtr<BacktraceMap> map; + if (share_map) { + map.reset(BacktraceMap::Create(pid)); + } + UniquePtr<Backtrace> backtrace(Backtrace::Create(pid, tid, map.get())); + ASSERT_TRUE(backtrace->Unwind(0)); + ASSERT_TRUE(backtrace.get() != NULL); + if (ReadyFunc(backtrace.get())) { + VerifyFunc(backtrace.get()); + verified = true; + } + + ASSERT_TRUE(ptrace(PTRACE_DETACH, ptrace_tid, 0, 0) == 0); + } + // If 5 seconds have passed, then we are done. + } while (!verified && (NanoTime() - start) <= 5 * NS_PER_SEC); + ASSERT_TRUE(verified); +} + +TEST(libbacktrace, ptrace_trace) { + pid_t pid; + if ((pid = fork()) == 0) { + ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0); + exit(1); + } + VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyLevelDump); + + kill(pid, SIGKILL); + int status; + ASSERT_EQ(waitpid(pid, &status, 0), pid); +} + +TEST(libbacktrace, ptrace_trace_shared_map) { + pid_t pid; + if ((pid = fork()) == 0) { + ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0); + exit(1); + } + + VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, true, ReadyLevelBacktrace, VerifyLevelDump); + + kill(pid, SIGKILL); + int status; + ASSERT_EQ(waitpid(pid, &status, 0), pid); +} + +TEST(libbacktrace, ptrace_max_trace) { + pid_t pid; + if ((pid = fork()) == 0) { + ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, NULL, NULL), 0); + exit(1); + } + VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyMaxBacktrace, VerifyMaxDump); + + kill(pid, SIGKILL); + int status; + ASSERT_EQ(waitpid(pid, &status, 0), pid); +} + +void VerifyProcessIgnoreFrames(Backtrace* bt_all) { + UniquePtr<Backtrace> ign1(Backtrace::Create(bt_all->Pid(), BACKTRACE_CURRENT_THREAD)); + ASSERT_TRUE(ign1.get() != NULL); + ASSERT_TRUE(ign1->Unwind(1)); + + UniquePtr<Backtrace> ign2(Backtrace::Create(bt_all->Pid(), BACKTRACE_CURRENT_THREAD)); + ASSERT_TRUE(ign2.get() != NULL); + ASSERT_TRUE(ign2->Unwind(2)); + + VerifyIgnoreFrames(bt_all, ign1.get(), ign2.get(), NULL); +} + +TEST(libbacktrace, ptrace_ignore_frames) { + pid_t pid; + if ((pid = fork()) == 0) { + ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0); + exit(1); + } + VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyProcessIgnoreFrames); + + kill(pid, SIGKILL); + int status; + ASSERT_EQ(waitpid(pid, &status, 0), pid); +} + +// Create a process with multiple threads and dump all of the threads. +void* PtraceThreadLevelRun(void*) { + EXPECT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0); + return NULL; +} + +void GetThreads(pid_t pid, std::vector<pid_t>* threads) { + // Get the list of tasks. + char task_path[128]; + snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid); + + DIR* tasks_dir = opendir(task_path); + ASSERT_TRUE(tasks_dir != NULL); + struct dirent* entry; + while ((entry = readdir(tasks_dir)) != NULL) { + char* end; + pid_t tid = strtoul(entry->d_name, &end, 10); + if (*end == '\0') { + threads->push_back(tid); + } + } + closedir(tasks_dir); +} + +TEST(libbacktrace, ptrace_threads) { + pid_t pid; + if ((pid = fork()) == 0) { + for (size_t i = 0; i < NUM_PTRACE_THREADS; i++) { + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + pthread_t thread; + ASSERT_TRUE(pthread_create(&thread, &attr, PtraceThreadLevelRun, NULL) == 0); + } + ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0); + exit(1); + } + + // Check to see that all of the threads are running before unwinding. + std::vector<pid_t> threads; + uint64_t start = NanoTime(); + do { + usleep(US_PER_MSEC); + threads.clear(); + GetThreads(pid, &threads); + } while ((threads.size() != NUM_PTRACE_THREADS + 1) && + ((NanoTime() - start) <= 5 * NS_PER_SEC)); + ASSERT_EQ(threads.size(), static_cast<size_t>(NUM_PTRACE_THREADS + 1)); + + ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0); + WaitForStop(pid); + for (std::vector<int>::const_iterator it = threads.begin(); it != threads.end(); ++it) { + // Skip the current forked process, we only care about the threads. + if (pid == *it) { + continue; + } + VerifyProcTest(pid, *it, false, ReadyLevelBacktrace, VerifyLevelDump); + } + ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0); + + kill(pid, SIGKILL); + int status; + ASSERT_EQ(waitpid(pid, &status, 0), pid); +} + +void VerifyLevelThread(void*) { + UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid())); + ASSERT_TRUE(backtrace.get() != NULL); + ASSERT_TRUE(backtrace->Unwind(0)); + + VerifyLevelDump(backtrace.get()); +} + +TEST(libbacktrace, thread_current_level) { + ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelThread, NULL), 0); +} + +void VerifyMaxThread(void*) { + UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid())); + ASSERT_TRUE(backtrace.get() != NULL); + ASSERT_TRUE(backtrace->Unwind(0)); + + VerifyMaxDump(backtrace.get()); +} + +TEST(libbacktrace, thread_current_max) { + ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxThread, NULL), 0); +} + +void* ThreadLevelRun(void* data) { + thread_t* thread = reinterpret_cast<thread_t*>(data); + + thread->tid = gettid(); + EXPECT_NE(test_level_one(1, 2, 3, 4, ThreadSetState, data), 0); + return NULL; +} + +TEST(libbacktrace, thread_level_trace) { + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + thread_t thread_data = { 0, 0, 0 }; + pthread_t thread; + ASSERT_TRUE(pthread_create(&thread, &attr, ThreadLevelRun, &thread_data) == 0); + + // Wait up to 2 seconds for the tid to be set. + ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2)); + + // Save the current signal action and make sure it is restored afterwards. + struct sigaction cur_action; + ASSERT_TRUE(sigaction(SIGURG, NULL, &cur_action) == 0); + + UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid)); + ASSERT_TRUE(backtrace.get() != NULL); + ASSERT_TRUE(backtrace->Unwind(0)); + + VerifyLevelDump(backtrace.get()); + + // Tell the thread to exit its infinite loop. + android_atomic_acquire_store(0, &thread_data.state); + + // Verify that the old action was restored. + struct sigaction new_action; + ASSERT_TRUE(sigaction(SIGURG, NULL, &new_action) == 0); + EXPECT_EQ(cur_action.sa_sigaction, new_action.sa_sigaction); + EXPECT_EQ(cur_action.sa_flags, new_action.sa_flags); +} + +TEST(libbacktrace, thread_ignore_frames) { + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + thread_t thread_data = { 0, 0, 0 }; + pthread_t thread; + ASSERT_TRUE(pthread_create(&thread, &attr, ThreadLevelRun, &thread_data) == 0); + + // Wait up to 2 seconds for the tid to be set. + ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2)); + + UniquePtr<Backtrace> all(Backtrace::Create(getpid(), thread_data.tid)); + ASSERT_TRUE(all.get() != NULL); + ASSERT_TRUE(all->Unwind(0)); + + UniquePtr<Backtrace> ign1(Backtrace::Create(getpid(), thread_data.tid)); + ASSERT_TRUE(ign1.get() != NULL); + ASSERT_TRUE(ign1->Unwind(1)); + + UniquePtr<Backtrace> ign2(Backtrace::Create(getpid(), thread_data.tid)); + ASSERT_TRUE(ign2.get() != NULL); + ASSERT_TRUE(ign2->Unwind(2)); + + VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), NULL); + + // Tell the thread to exit its infinite loop. + android_atomic_acquire_store(0, &thread_data.state); +} + +void* ThreadMaxRun(void* data) { + thread_t* thread = reinterpret_cast<thread_t*>(data); + + thread->tid = gettid(); + EXPECT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, ThreadSetState, data), 0); + return NULL; +} + +TEST(libbacktrace, thread_max_trace) { + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + thread_t thread_data = { 0, 0, 0 }; + pthread_t thread; + ASSERT_TRUE(pthread_create(&thread, &attr, ThreadMaxRun, &thread_data) == 0); + + // Wait for the tid to be set. + ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2)); + + UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid)); + ASSERT_TRUE(backtrace.get() != NULL); + ASSERT_TRUE(backtrace->Unwind(0)); + + VerifyMaxDump(backtrace.get()); + + // Tell the thread to exit its infinite loop. + android_atomic_acquire_store(0, &thread_data.state); +} + +void* ThreadDump(void* data) { + dump_thread_t* dump = reinterpret_cast<dump_thread_t*>(data); + while (true) { + if (android_atomic_acquire_load(dump->now)) { + break; + } + } + + // The status of the actual unwind will be checked elsewhere. + dump->backtrace = Backtrace::Create(getpid(), dump->thread.tid); + dump->backtrace->Unwind(0); + + android_atomic_acquire_store(1, &dump->done); + + return NULL; +} + +TEST(libbacktrace, thread_multiple_dump) { + // Dump NUM_THREADS simultaneously. + std::vector<thread_t> runners(NUM_THREADS); + std::vector<dump_thread_t> dumpers(NUM_THREADS); + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + for (size_t i = 0; i < NUM_THREADS; i++) { + // Launch the runners, they will spin in hard loops doing nothing. + runners[i].tid = 0; + runners[i].state = 0; + ASSERT_TRUE(pthread_create(&runners[i].threadId, &attr, ThreadMaxRun, &runners[i]) == 0); + } + + // Wait for tids to be set. + for (std::vector<thread_t>::iterator it = runners.begin(); it != runners.end(); ++it) { + ASSERT_TRUE(WaitForNonZero(&it->state, 10)); + } + + // Start all of the dumpers at once, they will spin until they are signalled + // to begin their dump run. + int32_t dump_now = 0; + for (size_t i = 0; i < NUM_THREADS; i++) { + dumpers[i].thread.tid = runners[i].tid; + dumpers[i].thread.state = 0; + dumpers[i].done = 0; + dumpers[i].now = &dump_now; + + ASSERT_TRUE(pthread_create(&dumpers[i].thread.threadId, &attr, ThreadDump, &dumpers[i]) == 0); + } + + // Start all of the dumpers going at once. + android_atomic_acquire_store(1, &dump_now); + + for (size_t i = 0; i < NUM_THREADS; i++) { + ASSERT_TRUE(WaitForNonZero(&dumpers[i].done, 10)); + + // Tell the runner thread to exit its infinite loop. + android_atomic_acquire_store(0, &runners[i].state); + + ASSERT_TRUE(dumpers[i].backtrace != NULL); + VerifyMaxDump(dumpers[i].backtrace); + + delete dumpers[i].backtrace; + dumpers[i].backtrace = NULL; + } +} + +// This test is for UnwindMaps that should share the same map cursor when +// multiple maps are created for the current process at the same time. +TEST(libbacktrace, simultaneous_maps) { + BacktraceMap* map1 = BacktraceMap::Create(getpid()); + BacktraceMap* map2 = BacktraceMap::Create(getpid()); + BacktraceMap* map3 = BacktraceMap::Create(getpid()); + + Backtrace* back1 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map1); + EXPECT_TRUE(back1->Unwind(0)); + delete back1; + delete map1; + + Backtrace* back2 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map2); + EXPECT_TRUE(back2->Unwind(0)); + delete back2; + delete map2; + + Backtrace* back3 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map3); + EXPECT_TRUE(back3->Unwind(0)); + delete back3; + delete map3; +} + +TEST(libbacktrace, format_test) { + UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD)); + ASSERT_TRUE(backtrace.get() != NULL); + + backtrace_frame_data_t frame; + frame.num = 1; + frame.pc = 2; + frame.sp = 0; + frame.stack_size = 0; + frame.map = NULL; + frame.func_offset = 0; + + backtrace_map_t map; + map.start = 0; + map.end = 0; + + // Check no map set. + frame.num = 1; +#if defined(__LP64__) + EXPECT_EQ("#01 pc 0000000000000002 <unknown>", +#else + EXPECT_EQ("#01 pc 00000002 <unknown>", +#endif + backtrace->FormatFrameData(&frame)); + + // Check map name empty, but exists. + frame.map = ↦ + map.start = 1; +#if defined(__LP64__) + EXPECT_EQ("#01 pc 0000000000000001 <unknown>", +#else + EXPECT_EQ("#01 pc 00000001 <unknown>", +#endif + backtrace->FormatFrameData(&frame)); + + + // Check relative pc is set and map name is set. + frame.pc = 0x12345679; + frame.map = ↦ + map.name = "MapFake"; + map.start = 1; +#if defined(__LP64__) + EXPECT_EQ("#01 pc 0000000012345678 MapFake", +#else + EXPECT_EQ("#01 pc 12345678 MapFake", +#endif + backtrace->FormatFrameData(&frame)); + + // Check func_name is set, but no func offset. + frame.func_name = "ProcFake"; +#if defined(__LP64__) + EXPECT_EQ("#01 pc 0000000012345678 MapFake (ProcFake)", +#else + EXPECT_EQ("#01 pc 12345678 MapFake (ProcFake)", +#endif + backtrace->FormatFrameData(&frame)); + + // Check func_name is set, and func offset is non-zero. + frame.func_offset = 645; +#if defined(__LP64__) + EXPECT_EQ("#01 pc 0000000012345678 MapFake (ProcFake+645)", +#else + EXPECT_EQ("#01 pc 12345678 MapFake (ProcFake+645)", +#endif + backtrace->FormatFrameData(&frame)); +} diff --git a/libbacktrace/backtrace_testlib.c b/libbacktrace/backtrace_testlib.c new file mode 100644 index 0000000..d4d15db --- /dev/null +++ b/libbacktrace/backtrace_testlib.c @@ -0,0 +1,55 @@ +/* + * 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. + */ + +#include <stdio.h> + +int test_level_four(int one, int two, int three, int four, + void (*callback_func)(void*), void* data) { + if (callback_func != NULL) { + callback_func(data); + } else { + while (1) { + } + } + return one + two + three + four; +} + +int test_level_three(int one, int two, int three, int four, + void (*callback_func)(void*), void* data) { + return test_level_four(one+3, two+6, three+9, four+12, callback_func, data) + 3; +} + +int test_level_two(int one, int two, int three, int four, + void (*callback_func)(void*), void* data) { + return test_level_three(one+2, two+4, three+6, four+8, callback_func, data) + 2; +} + +int test_level_one(int one, int two, int three, int four, + void (*callback_func)(void*), void* data) { + return test_level_two(one+1, two+2, three+3, four+4, callback_func, data) + 1; +} + +int test_recursive_call(int level, void (*callback_func)(void*), void* data) { + if (level > 0) { + return test_recursive_call(level - 1, callback_func, data) + level; + } else if (callback_func != NULL) { + callback_func(data); + } else { + while (1) { + } + } + return 0; +} diff --git a/libbacktrace/thread_utils.c b/libbacktrace/thread_utils.c new file mode 100644 index 0000000..6f4cd3c --- /dev/null +++ b/libbacktrace/thread_utils.c @@ -0,0 +1,42 @@ +/* + * 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. + */ + +#include "thread_utils.h" + +#if defined(__APPLE__) + +#include <sys/syscall.h> + +// Mac OS >= 10.6 has a system call equivalent to Linux's gettid(). +pid_t gettid() { + return syscall(SYS_thread_selfid); +} + +#elif !defined(__BIONIC__) + +// glibc doesn't implement or export either gettid or tgkill. +#include <unistd.h> +#include <sys/syscall.h> + +pid_t gettid() { + return syscall(__NR_gettid); +} + +int tgkill(int tgid, int tid, int sig) { + return syscall(__NR_tgkill, tgid, tid, sig); +} + +#endif diff --git a/libcutils/list.c b/libbacktrace/thread_utils.h index e13452d..ae4c929 100644 --- a/libcutils/list.c +++ b/libbacktrace/thread_utils.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * 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. @@ -14,24 +14,17 @@ * limitations under the License. */ -#include <cutils/list.h> +#ifndef _LIBBACKTRACE_THREAD_UTILS_H +#define _LIBBACKTRACE_THREAD_UTILS_H -void list_init(struct listnode *node) -{ - node->next = node; - node->prev = node; -} +#include <unistd.h> -void list_add_tail(struct listnode *head, struct listnode *item) -{ - item->next = head; - item->prev = head->prev; - head->prev->next = item; - head->prev = item; -} +__BEGIN_DECLS -void list_remove(struct listnode *item) -{ - item->next->prev = item->prev; - item->prev->next = item->next; -} +int tgkill(int tgid, int tid, int sig); + +pid_t gettid(); + +__END_DECLS + +#endif /* _LIBBACKTRACE_THREAD_UTILS_H */ diff --git a/libcorkscrew/Android.mk b/libcorkscrew/Android.mk index d62c2d5..e275317 100644 --- a/libcorkscrew/Android.mk +++ b/libcorkscrew/Android.mk @@ -51,7 +51,7 @@ endif LOCAL_SHARED_LIBRARIES += libdl libcutils liblog libgccdemangle -LOCAL_CFLAGS += -std=gnu99 -Werror +LOCAL_CFLAGS += -std=gnu99 -Werror -Wno-unused-parameter LOCAL_MODULE := libcorkscrew LOCAL_MODULE_TAGS := optional @@ -81,7 +81,7 @@ ifeq ($(HOST_OS),linux) LOCAL_SHARED_LIBRARIES += libgccdemangle # TODO: is this even needed on Linux? LOCAL_LDLIBS += -lrt endif -LOCAL_CFLAGS += -std=gnu99 -Werror +LOCAL_CFLAGS += -std=gnu99 -Werror -Wno-unused-parameter LOCAL_MODULE := libcorkscrew LOCAL_MODULE_TAGS := optional include $(BUILD_HOST_SHARED_LIBRARY) diff --git a/libcorkscrew/arch-arm/backtrace-arm.c b/libcorkscrew/arch-arm/backtrace-arm.c index ff6c192..7bd0d8f 100644 --- a/libcorkscrew/arch-arm/backtrace-arm.c +++ b/libcorkscrew/arch-arm/backtrace-arm.c @@ -59,7 +59,7 @@ #include <limits.h> #include <errno.h> #include <sys/ptrace.h> -#include <sys/exec_elf.h> +#include <elf.h> #include <cutils/log.h> #if !defined(__BIONIC_HAVE_UCONTEXT_T) diff --git a/libcorkscrew/arch-arm/ptrace-arm.c b/libcorkscrew/arch-arm/ptrace-arm.c index 78a9ea9..a50844e 100644 --- a/libcorkscrew/arch-arm/ptrace-arm.c +++ b/libcorkscrew/arch-arm/ptrace-arm.c @@ -19,7 +19,7 @@ #include "../ptrace-arch.h" -#include <sys/exec_elf.h> +#include <elf.h> #include <cutils/log.h> #ifndef PT_ARM_EXIDX diff --git a/libcorkscrew/arch-mips/backtrace-mips.c b/libcorkscrew/arch-mips/backtrace-mips.c index 1abdd83..57cb324 100644 --- a/libcorkscrew/arch-mips/backtrace-mips.c +++ b/libcorkscrew/arch-mips/backtrace-mips.c @@ -23,20 +23,34 @@ #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__) + +#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. */ + /* For PTRACE_GETREGS */ typedef struct { - /* FIXME: check this definition */ uint64_t regs[32]; uint64_t lo; uint64_t hi; @@ -46,22 +60,48 @@ typedef struct { uint64_t cause; } user_regs_struct; +enum { + REG_ZERO = 0, REG_AT, REG_V0, REG_V1, + REG_A0, REG_A1, REG_A2, REG_A3, + REG_T0, REG_T1, REG_T2, REG_T3, + REG_T4, REG_T5, REG_T6, REG_T7, + REG_S0, REG_S1, REG_S2, REG_S3, + REG_S4, REG_S5, REG_S6, REG_S7, + REG_T8, REG_T9, REG_K0, REG_K1, + REG_GP, REG_SP, REG_S8, REG_RA, +}; + /* Machine context at the time a signal was raised. */ typedef struct ucontext { - /* FIXME: use correct definition */ - uint32_t sp; - uint32_t ra; - uint32_t pc; + unsigned int sc_regmask; + unsigned int sc_status; + unsigned long long sc_pc; + unsigned long long sc_regs[32]; + unsigned long long sc_fpregs[32]; + unsigned int sc_acx; + unsigned int sc_fpc_csr; + unsigned int sc_fpc_eir; + unsigned int sc_used_math; + unsigned int sc_dsp; + unsigned long long sc_mdhi; + unsigned long long sc_mdlo; + unsigned long sc_hi1; + unsigned long sc_lo1; + unsigned long sc_hi2; + unsigned long sc_lo2; + unsigned long sc_hi3; + unsigned long sc_lo3; } ucontext_t; +#endif /* __BIONIC_HAVE_UCONTEXT_T */ +#endif + /* Unwind state. */ typedef struct { - uint32_t sp; - uint32_t ra; - uint32_t pc; + uint32_t reg[DWARF_REGISTERS]; } unwind_state_t; -uintptr_t rewind_pc_arch(const memory_t* memory, uintptr_t pc) { +uintptr_t rewind_pc_arch(const memory_t* memory __attribute__((unused)), uintptr_t pc) { if (pc == 0) return pc; if ((pc & 1) == 0) @@ -69,108 +109,806 @@ uintptr_t rewind_pc_arch(const memory_t* memory, uintptr_t pc) { return pc; } -static ssize_t unwind_backtrace_common(const memory_t* memory, - const map_info_t* map_info_list, - unwind_state_t* state, backtrace_frame_t* backtrace, - size_t ignore_depth, size_t max_depth) { - size_t ignored_frames = 0; - size_t returned_frames = 0; +/* Read byte through 4 byte cache. Usually we read byte by byte and updating cursor. */ +static bool try_get_byte(const memory_t* memory, uintptr_t ptr, uint8_t* out_value, uint32_t* cursor) { + static uintptr_t lastptr; + static uint32_t buf; - for (size_t index = 0; returned_frames < max_depth; index++) { - uintptr_t pc = index ? rewind_pc_arch(memory, state->pc) : state->pc; - backtrace_frame_t* frame; - uintptr_t addr; - int maxcheck = 1024; - int stack_size = 0, ra_offset = 0; - bool found_start = false; + ptr += *cursor; - frame = add_backtrace_entry(pc, backtrace, ignore_depth, - max_depth, &ignored_frames, &returned_frames); + 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; +} - if (frame) - frame->stack_top = state->sp; +/* 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; +} - ALOGV("#%d: frame=%p pc=%08x sp=%08x\n", - index, frame, frame->absolute_pc, frame->stack_top); +/* 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; +} - for (addr = state->pc; maxcheck-- > 0 && !found_start; addr -= 4) { - uint32_t op; - if (!try_get_word(memory, addr, &op)) - break; +/* 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); +} - // ALOGV("@0x%08x: 0x%08x\n", addr, op); - switch (op & 0xffff0000) { - case 0x27bd0000: // addiu sp, imm - { - // looking for stack being decremented - int32_t immediate = ((((int)op) << 16) >> 16); - if (immediate < 0) { - stack_size = -immediate; - found_start = true; - ALOGV("@0x%08x: found stack adjustment=%d\n", addr, stack_size); - } - } - break; - case 0xafbf0000: // sw ra, imm(sp) - ra_offset = ((((int)op) << 16) >> 16); - ALOGV("@0x%08x: found ra offset=%d\n", addr, ra_offset); - break; - case 0x3c1c0000: // lui gp - ALOGV("@0x%08x: found function boundary\n", addr); - found_start = true; - break; - default: - break; - } +/* 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; +} - if (ra_offset) { - uint32_t next_ra; - if (!try_get_word(memory, state->sp + ra_offset, &next_ra)) - break; - state->ra = next_ra; - ALOGV("New ra: 0x%08x\n", state->ra); +/* 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 mips. + 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 mips. + 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 mips. + if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; + if (reg >= DWARF_REGISTERS) { + ALOGE("DW_CFA_restore_extended: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS); + return false; + } + dstate->regs[reg].rule = stack->regs[reg].rule; + dstate->regs[reg].value = stack->regs[reg].value; + 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 mips. + if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; + if (reg >= DWARF_REGISTERS) { + ALOGE("DW_CFA_undefined: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS); + return false; + } + dstate->regs[reg].rule = 'u'; + dstate->regs[reg].value = 0; + ALOGV("DW_CFA_undefined: r%d", reg); + break; + case DW_CFA_same_value: // probably we don't have it on mips. + if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; + if (reg >= DWARF_REGISTERS) { + ALOGE("DW_CFA_undefined: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS); + return false; + } + dstate->regs[reg].rule = 's'; + dstate->regs[reg].value = 0; + ALOGV("DW_CFA_same_value: r%d", reg); + break; + case DW_CFA_register: // probably we don't have it on mips. + 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; +} - if (stack_size) { - if (frame) - frame->stack_size = stack_size; - state->sp += stack_size; - ALOGV("New sp: 0x%08x\n", state->sp); +/* 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 SP if it's not updated by dwarf rule we assume it's equal to CFA + // for PC if it's not updated by dwarf rule we assume it's equal to RA + if (reg == DWARF_SP) { + ALOGV("get_old_register_value: adjusting sp to CFA: 0x%x", cfa); + newstate->reg[reg] = cfa; + } else if (reg == DWARF_PC) { + ALOGV("get_old_register_value: adjusting PC to RA: 0x%x", newstate->reg[DWARF_RA]); + newstate->reg[reg] = newstate->reg[DWARF_RA]; + } 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) { + 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_SP) { + cfa = state->reg[DWARF_SP] + dstate->cfa_off; + } else if (dstate->cfa_reg == DWARF_FP) { + cfa = state->reg[DWARF_FP] + dstate->cfa_off; + } else { + ALOGE("update_state: unexpected CFA register: %d", dstate->cfa_reg); + return false; + } + ALOGV("update_state: new CFA: 0x%x", cfa); - if (state->pc == state->ra && stack_size == 0) + /* Update registers. Order is important to allow RA to propagate to PC */ + /* Getting FP. */ + if (!get_old_register_value(memory, cfa, dstate, DWARF_FP, state, &newstate)) return false; + /* Getting SP. */ + if (!get_old_register_value(memory, cfa, dstate, DWARF_SP, state, &newstate)) return false; + /* Getting RA. */ + if (!get_old_register_value(memory, cfa, dstate, DWARF_RA, state, &newstate)) return false; + /* Getting PC. */ + if (!get_old_register_value(memory, cfa, dstate, DWARF_PC, state, &newstate)) return false; + + ALOGV("update_state: PC: 0x%x; restore PC: 0x%x", state->reg[DWARF_PC], newstate.reg[DWARF_PC]); + ALOGV("update_state: RA: 0x%x; restore RA: 0x%x", state->reg[DWARF_RA], newstate.reg[DWARF_RA]); + ALOGV("update_state: FP: 0x%x; restore FP: 0x%x", state->reg[DWARF_FP], newstate.reg[DWARF_FP]); + ALOGV("update_state: SP: 0x%x; restore SP: 0x%x", state->reg[DWARF_SP], newstate.reg[DWARF_SP]); + + if (newstate.reg[DWARF_PC] == 0) + return false; + + /* End backtrace if registers do not change */ + if ((state->reg[DWARF_PC] == newstate.reg[DWARF_PC]) && + (state->reg[DWARF_RA] == newstate.reg[DWARF_RA]) && + (state->reg[DWARF_FP] == newstate.reg[DWARF_FP]) && + (state->reg[DWARF_SP] == newstate.reg[DWARF_SP])) + return false; + + *state = newstate; + return true; +} + +/* Execute CIE and FDE instructions for FDE found with find_fde. */ +static bool execute_fde(const memory_t* memory, + 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_PC] >= dstate->loc) { + if (!execute_dwarf(memory, fde, cie_info, dstate, &c, stack, &stack_ptr)) { + return false; + } + ALOGV("PC: %x, LOC: %x", state->reg[DWARF_PC], dstate->loc); + } + + return update_state(memory, state, dstate); +} + +static bool heuristic_state_update(const memory_t* memory, unwind_state_t* state) +{ + bool found_start = false; + int maxcheck = 1024; + int32_t stack_size = 0; + int32_t ra_offset = 0; + dwarf_state_t dwarf_state; + dwarf_state_t* dstate = &dwarf_state; + + static struct { + uint32_t insn; + uint32_t mask; + } frame0sig[] = { + {0x3c1c0000, 0xffff0000}, /* lui gp,xxxx */ + {0x279c0000, 0xffff0000}, /* addiu gp,gp,xxxx */ + {0x039fe021, 0xffffffff}, /* addu gp,gp,ra */ + }; + const int nframe0sig = sizeof(frame0sig)/sizeof(frame0sig[0]); + int f0 = nframe0sig; + memset(dstate, 0, sizeof(dwarf_state_t)); + + /* Search code backwards looking for function prologue */ + for (uint32_t pc = state->reg[DWARF_PC]-4; maxcheck-- > 0 && !found_start; pc -= 4) { + uint32_t op; + int32_t immediate; + + if (!try_get_word(memory, pc, &op)) + return false; + + // ALOGV("@0x%08x: 0x%08x\n", pc, op); + + // Check for frame 0 signature + if ((op & frame0sig[f0].mask) == frame0sig[f0].insn) { + if (f0 == 0) + return false; + f0--; + } + else { + f0 = nframe0sig; + } + + switch (op & 0xffff0000) { + case 0x27bd0000: // addiu sp, imm + // looking for stack being decremented + immediate = (((int32_t)op) << 16) >> 16; + if (immediate < 0) { + stack_size = -immediate; + ALOGV("@0x%08x: found stack adjustment=%d\n", pc, stack_size); + } break; + case 0x039f0000: // e021 - if (state->ra == 0) + case 0xafbf0000: // sw ra, imm(sp) + ra_offset = (((int32_t)op) << 16) >> 16; + ALOGV("@0x%08x: found ra offset=%d\n", pc, ra_offset); break; + case 0x3c1c0000: // lui gp + ALOGV("@0x%08x: found function boundary", pc); + found_start = true; + break; + default: + break; + } + } + + dstate->cfa_reg = DWARF_SP; + dstate->cfa_off = stack_size; - state->pc = state->ra; + if (ra_offset) { + dstate->regs[DWARF_RA].rule = 'o'; + dstate->regs[DWARF_RA].value = -stack_size + ra_offset; } - ALOGV("returning %d frames\n", returned_frames); + return update_state(memory, state, dstate); +} + +static ssize_t unwind_backtrace_common(const memory_t* memory, + const map_info_t* map_info_list, + unwind_state_t* state, backtrace_frame_t* backtrace, + size_t ignore_depth, size_t max_depth) { + + size_t ignored_frames = 0; + size_t returned_frames = 0; + + ALOGV("Unwinding tid: %d", memory->tid); + ALOGV("PC: %x", state->reg[DWARF_PC]); + ALOGV("RA: %x", state->reg[DWARF_RA]); + ALOGV("FP: %x", state->reg[DWARF_FP]); + ALOGV("SP: %x", state->reg[DWARF_SP]); + for (size_t index = 0; returned_frames < max_depth; index++) { + uintptr_t fde = find_fde(memory, map_info_list, state->reg[DWARF_PC]); + backtrace_frame_t* frame = add_backtrace_entry( + index ? rewind_pc_arch(memory, state->reg[DWARF_PC]) : state->reg[DWARF_PC], + backtrace, ignore_depth, max_depth, + &ignored_frames, &returned_frames); + uint32_t stack_top = state->reg[DWARF_SP]; + + if (fde) { + /* Use FDE to update state */ + if (!execute_fde(memory, fde, state)) + break; + } + else { + /* FDE is not found, update state heuristically */ + if (!heuristic_state_update(memory, state)) + break; + } + + if (frame) { + frame->stack_top = stack_top; + if (stack_top < state->reg[DWARF_SP]) { + frame->stack_size = state->reg[DWARF_SP] - stack_top; + } + } + ALOGV("Stack: 0x%x ... 0x%x - %d bytes", frame->stack_top, state->reg[DWARF_SP], frame->stack_size); + } return returned_frames; } -ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo, void* sigcontext, +ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo __attribute__((unused)), void* sigcontext, const map_info_t* map_info_list, backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) { const ucontext_t* uc = (const ucontext_t*)sigcontext; unwind_state_t state; - state.sp = uc->sp; - state.pc = uc->pc; - state.ra = uc->ra; + state.reg[DWARF_PC] = uc->sc_pc; + state.reg[DWARF_RA] = uc->sc_regs[REG_RA]; + state.reg[DWARF_FP] = uc->sc_regs[REG_S8]; + state.reg[DWARF_SP] = uc->sc_regs[REG_SP]; 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); + ignore_depth, max_depth, state.reg[DWARF_PC], state.reg[DWARF_SP], state.reg[DWARF_RA]); memory_t memory; init_memory(&memory, map_info_list); return unwind_backtrace_common(&memory, map_info_list, - &state, backtrace, ignore_depth, max_depth); + &state, backtrace, ignore_depth, max_depth); } ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context, @@ -182,16 +920,17 @@ ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context, } unwind_state_t state; - state.sp = regs.regs[29]; - state.ra = regs.regs[31]; - state.pc = regs.epc; + state.reg[DWARF_PC] = regs.epc; + state.reg[DWARF_RA] = regs.regs[REG_RA]; + state.reg[DWARF_FP] = regs.regs[REG_S8]; + state.reg[DWARF_SP] = regs.regs[REG_SP]; 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); + ignore_depth, max_depth, state.reg[DWARF_PC], state.reg[DWARF_SP], state.reg[DWARF_RA]); memory_t memory; init_memory_ptrace(&memory, tid); return unwind_backtrace_common(&memory, context->map_info_list, - &state, backtrace, ignore_depth, max_depth); + &state, backtrace, ignore_depth, max_depth); } diff --git a/libcorkscrew/arch-mips/dwarf.h b/libcorkscrew/arch-mips/dwarf.h new file mode 100644 index 0000000..8504ea0 --- /dev/null +++ b/libcorkscrew/arch-mips/dwarf.h @@ -0,0 +1,187 @@ +/* + * 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 mips */ +typedef enum + { + UNW_MIPS_R0, + UNW_MIPS_R1, + UNW_MIPS_R2, + UNW_MIPS_R3, + UNW_MIPS_R4, + UNW_MIPS_R5, + UNW_MIPS_R6, + UNW_MIPS_R7, + UNW_MIPS_R8, + UNW_MIPS_R9, + UNW_MIPS_R10, + UNW_MIPS_R11, + UNW_MIPS_R12, + UNW_MIPS_R13, + UNW_MIPS_R14, + UNW_MIPS_R15, + UNW_MIPS_R16, + UNW_MIPS_R17, + UNW_MIPS_R18, + UNW_MIPS_R19, + UNW_MIPS_R20, + UNW_MIPS_R21, + UNW_MIPS_R22, + UNW_MIPS_R23, + UNW_MIPS_R24, + UNW_MIPS_R25, + UNW_MIPS_R26, + UNW_MIPS_R27, + UNW_MIPS_R28, + UNW_MIPS_R29, + UNW_MIPS_R30, + UNW_MIPS_R31, + + UNW_MIPS_PC = 34, + + /* FIXME: Other registers! */ + + /* For MIPS, the CFA is the value of SP (r29) at the call site in the + previous frame. */ + UNW_MIPS_CFA, + + UNW_TDEP_LASTREG, + + UNW_TDEP_LAST_REG = UNW_MIPS_R31, + + UNW_TDEP_IP = UNW_MIPS_R31, + UNW_TDEP_SP = UNW_MIPS_R29, + UNW_TDEP_EH = UNW_MIPS_R0 /* FIXME. */ + + } +mips_regnum_t; + +#define DWARF_REGISTERS UNW_TDEP_LASTREG + +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 mips +} dwarf_state_t; + +/* DWARF registers we are caring about. */ + + +#define DWARF_SP UNW_MIPS_R29 +#define DWARF_RA UNW_MIPS_R31 +#define DWARF_PC UNW_MIPS_PC +#define DWARF_FP UNW_MIPS_CFA /* FIXME is this correct? */ diff --git a/libcorkscrew/arch-mips/ptrace-mips.c b/libcorkscrew/arch-mips/ptrace-mips.c index f0ea110..ba3b60a 100644 --- a/libcorkscrew/arch-mips/ptrace-mips.c +++ b/libcorkscrew/arch-mips/ptrace-mips.c @@ -19,10 +19,59 @@ #include "../ptrace-arch.h" +#include <stddef.h> +#include <elf.h> #include <cutils/log.h> +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; + + + try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phoff), &elf_phoff); + ALOGV("reading 0x%08x elf_phoff:%x", mi->start + offsetof(Elf32_Ehdr, e_phoff), elf_phoff); + try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_ehsize), &elf_phentsize_ehsize); + ALOGV("reading 0x%08x elf_phentsize_ehsize:%x", 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); + ALOGV("reading 0x%08x elf_shentsize_phnum:%x", mi->start + offsetof(Elf32_Ehdr, e_phnum), 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) { + ALOGV("load_ptrace_map_info_data_arch"); + load_eh_frame_hdr(pid, mi, &data->eh_frame_hdr); } -void free_ptrace_map_info_data_arch(map_info_t* mi, map_info_data_t* data) { +void free_ptrace_map_info_data_arch(map_info_t* mi __attribute__((unused)), + map_info_data_t* data __attribute__((unused))) { + ALOGV("free_ptrace_map_info_data_arch"); } diff --git a/libcorkscrew/arch-x86/backtrace-x86.c b/libcorkscrew/arch-x86/backtrace-x86.c index e133ab6..ef22821 100755 --- a/libcorkscrew/arch-x86/backtrace-x86.c +++ b/libcorkscrew/arch-x86/backtrace-x86.c @@ -380,7 +380,7 @@ static bool execute_dwarf(const memory_t* memory, uintptr_t ptr, cie_info_t* cie 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) { + if (reg >= DWARF_REGISTERS) { ALOGE("DW_CFA_offset_extended: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS); return false; } @@ -390,39 +390,39 @@ static bool execute_dwarf(const memory_t* memory, uintptr_t ptr, cie_info_t* cie 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) { + if (reg >= DWARF_REGISTERS) { ALOGE("DW_CFA_restore_extended: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS); return false; } + dstate->regs[reg].rule = stack->regs[reg].rule; + dstate->regs[reg].value = stack->regs[reg].value; 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) { + if (reg >= DWARF_REGISTERS) { ALOGE("DW_CFA_undefined: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS); return false; } + dstate->regs[reg].rule = 'u'; + dstate->regs[reg].value = 0; 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) { + if (reg >= DWARF_REGISTERS) { ALOGE("DW_CFA_undefined: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS); return false; } + dstate->regs[reg].rule = 's'; + dstate->regs[reg].value = 0; 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) { + 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; } @@ -520,7 +520,7 @@ static bool get_old_register_value(const memory_t* memory, uint32_t cfa, /* 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) { + dwarf_state_t* dstate) { unwind_state_t newstate; /* We can restore more registers here if we need them. Meanwile doing minimal work here. */ /* Getting CFA. */ @@ -550,7 +550,6 @@ static bool update_state(const memory_t* memory, unwind_state_t* state, /* 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; @@ -753,7 +752,7 @@ static bool execute_fde(const memory_t* memory, ALOGV("IP: %x, LOC: %x", state->reg[DWARF_EIP], dstate->loc); } - return update_state(memory, state, dstate, cie_info); + return update_state(memory, state, dstate); } static ssize_t unwind_backtrace_common(const memory_t* memory, @@ -805,7 +804,7 @@ static ssize_t unwind_backtrace_common(const memory_t* memory, uint32_t stack_top = state->reg[DWARF_ESP]; - if (!execute_fde(memory, map_info_list, fde, state)) break; + if (!execute_fde(memory, fde, state)) break; if (frame) { frame->stack_top = stack_top; diff --git a/libcorkscrew/ptrace-arch.h b/libcorkscrew/ptrace-arch.h index 4451c29..0bcff63 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 __mips__ + uintptr_t eh_frame_hdr; #elif __i386__ uintptr_t eh_frame_hdr; #endif diff --git a/libcutils/Android.mk b/libcutils/Android.mk index 0fd5a57..93bccb0 100644 --- a/libcutils/Android.mk +++ b/libcutils/Android.mk @@ -37,7 +37,6 @@ commonSources := \ config_utils.c \ cpu_info.c \ load_file.c \ - list.c \ open_memstream.c \ strdup16to8.c \ strdup8to16.c \ @@ -96,11 +95,6 @@ include $(BUILD_HOST_STATIC_LIBRARY) # Shared and static library for target # ======================================================== -# This is needed in LOCAL_C_INCLUDES to access the C library's private -# header named <bionic_time.h> -# -libcutils_c_includes := bionic/libc/private - include $(CLEAR_VARS) LOCAL_MODULE := libcutils LOCAL_SRC_FILES := $(commonSources) \ @@ -115,21 +109,21 @@ LOCAL_SRC_FILES := $(commonSources) \ uevent.c ifeq ($(TARGET_ARCH),arm) -LOCAL_SRC_FILES += arch-arm/memset32.S + LOCAL_SRC_FILES += arch-arm/memset32.S else # !arm -ifeq ($(TARGET_ARCH_VARIANT),x86-atom) -LOCAL_CFLAGS += -DHAVE_MEMSET16 -DHAVE_MEMSET32 -LOCAL_SRC_FILES += arch-x86/android_memset16.S arch-x86/android_memset32.S memory.c -else # !x86-atom -ifeq ($(TARGET_ARCH),mips) -LOCAL_SRC_FILES += arch-mips/android_memset.c -else # !mips -LOCAL_SRC_FILES += memory.c -endif # !mips -endif # !x86-atom + ifeq ($(TARGET_ARCH),x86) + LOCAL_CFLAGS += -DHAVE_MEMSET16 -DHAVE_MEMSET32 + LOCAL_SRC_FILES += arch-x86/android_memset16.S arch-x86/android_memset32.S memory.c + else # !x86 + ifeq ($(TARGET_ARCH),mips) + LOCAL_SRC_FILES += arch-mips/android_memset.c + else # !mips + LOCAL_SRC_FILES += memory.c + endif # !mips + endif # !x86 endif # !arm -LOCAL_C_INCLUDES := $(libcutils_c_includes) $(KERNEL_HEADERS) +LOCAL_C_INCLUDES := $(libcutils_c_includes) LOCAL_STATIC_LIBRARIES := liblog LOCAL_CFLAGS += $(targetSmpFlag) include $(BUILD_STATIC_LIBRARY) diff --git a/libcutils/android_reboot.c b/libcutils/android_reboot.c index 16f82bb..b7895fa 100644 --- a/libcutils/android_reboot.c +++ b/libcutils/android_reboot.c @@ -16,6 +16,7 @@ #include <unistd.h> #include <sys/reboot.h> +#include <sys/syscall.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> @@ -24,6 +25,8 @@ #include <cutils/android_reboot.h> +#define UNUSED __attribute__((unused)) + /* Check to see if /proc/mounts contains any writeable filesystems * backed by a block device. * Return true if none found, else return false. @@ -101,7 +104,7 @@ static void remount_ro(void) } -int android_reboot(int cmd, int flags, char *arg) +int android_reboot(int cmd, int flags UNUSED, char *arg) { int ret; @@ -118,7 +121,7 @@ int android_reboot(int cmd, int flags, char *arg) break; case ANDROID_RB_RESTART2: - ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, + ret = syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, arg); break; diff --git a/libcutils/arch-x86/android_memset16.S b/libcutils/arch-x86/android_memset16.S index b1f09cb..f8b79bd 100644 --- a/libcutils/arch-x86/android_memset16.S +++ b/libcutils/arch-x86/android_memset16.S @@ -17,16 +17,9 @@ * Contributed by: Intel Corporation */ -#if defined(USE_SSE2) - # include "cache_wrapper.S" # undef __i686 # define USE_AS_ANDROID # define sse2_memset16_atom android_memset16 # include "sse2-memset16-atom.S" -#else - -# include "memset16.S" - -#endif diff --git a/libcutils/arch-x86/android_memset32.S b/libcutils/arch-x86/android_memset32.S index 1fb2ffe..6249fce 100644 --- a/libcutils/arch-x86/android_memset32.S +++ b/libcutils/arch-x86/android_memset32.S @@ -17,17 +17,9 @@ * Contributed by: Intel Corporation */ -#if defined(USE_SSE2) - # include "cache_wrapper.S" # undef __i686 # define USE_AS_ANDROID # define sse2_memset32_atom android_memset32 # include "sse2-memset32-atom.S" -#else - -# include "memset32.S" - -#endif - diff --git a/libcutils/arch-x86/sse2-memset16-atom.S b/libcutils/arch-x86/sse2-memset16-atom.S index cafec82..c2a762b 100644..100755 --- a/libcutils/arch-x86/sse2-memset16-atom.S +++ b/libcutils/arch-x86/sse2-memset16-atom.S @@ -86,7 +86,7 @@ name: \ # define SETRTNVAL movl DEST(%esp), %eax #endif -#ifdef SHARED +#if (defined SHARED || defined __PIC__) # define ENTRANCE PUSH (%ebx); # define RETURN_END POP (%ebx); ret # define RETURN RETURN_END; CFI_PUSH (%ebx) @@ -344,7 +344,7 @@ L(128bytesormore): PUSH (%ebx) mov $SHARED_CACHE_SIZE, %ebx #else -# ifdef SHARED +# if (defined SHARED || defined __PIC__) call __i686.get_pc_thunk.bx add $_GLOBAL_OFFSET_TABLE_, %ebx mov __x86_shared_cache_size@GOTOFF(%ebx), %ebx @@ -362,7 +362,7 @@ L(128bytesormore): # define RESTORE_EBX_STATE CFI_PUSH (%ebx) cmp $DATA_CACHE_SIZE, %ecx #else -# ifdef SHARED +# if (defined SHARED || defined __PIC__) # define RESTORE_EBX_STATE call __i686.get_pc_thunk.bx add $_GLOBAL_OFFSET_TABLE_, %ebx @@ -471,7 +471,7 @@ L(128bytesormore_nt): jae L(128bytesormore_nt) sfence L(shared_cache_loop_end): -#if defined DATA_CACHE_SIZE || !defined SHARED +#if defined DATA_CACHE_SIZE || !(defined SHARED || defined __PIC__) POP (%ebx) #endif add %ecx, %edx diff --git a/libcutils/arch-x86/sse2-memset32-atom.S b/libcutils/arch-x86/sse2-memset32-atom.S index 4a52484..05eb64f 100644..100755 --- a/libcutils/arch-x86/sse2-memset32-atom.S +++ b/libcutils/arch-x86/sse2-memset32-atom.S @@ -86,7 +86,7 @@ name: \ # define SETRTNVAL #endif -#ifdef SHARED +#if (defined SHARED || defined __PIC__) # define ENTRANCE PUSH (%ebx); # define RETURN_END POP (%ebx); ret # define RETURN RETURN_END; CFI_PUSH (%ebx) @@ -259,7 +259,7 @@ L(128bytesormore): PUSH (%ebx) mov $SHARED_CACHE_SIZE, %ebx #else -# ifdef SHARED +# if (defined SHARED || defined __PIC__) call __i686.get_pc_thunk.bx add $_GLOBAL_OFFSET_TABLE_, %ebx mov __x86_shared_cache_size@GOTOFF(%ebx), %ebx @@ -276,7 +276,7 @@ L(128bytesormore): # define RESTORE_EBX_STATE CFI_PUSH (%ebx) cmp $DATA_CACHE_SIZE, %ecx #else -# ifdef SHARED +# if (defined SHARED || defined __PIC__) # define RESTORE_EBX_STATE call __i686.get_pc_thunk.bx add $_GLOBAL_OFFSET_TABLE_, %ebx @@ -386,7 +386,7 @@ L(128bytesormore_nt): jae L(128bytesormore_nt) sfence L(shared_cache_loop_end): -#if defined DATA_CACHE_SIZE || !defined SHARED +#if defined DATA_CACHE_SIZE || !(defined SHARED || defined __PIC__) POP (%ebx) #endif add %ecx, %edx diff --git a/libcutils/ashmem-dev.c b/libcutils/ashmem-dev.c index 8b71f87..3089a94 100644 --- a/libcutils/ashmem-dev.c +++ b/libcutils/ashmem-dev.c @@ -48,7 +48,7 @@ int ashmem_create_region(const char *name, size_t size) return fd; if (name) { - char buf[ASHMEM_NAME_LEN]; + char buf[ASHMEM_NAME_LEN] = {0}; strlcpy(buf, name, sizeof(buf)); ret = ioctl(fd, ASHMEM_SET_NAME, buf); diff --git a/libcutils/debugger.c b/libcutils/debugger.c index 9425006..7d907fc 100644 --- a/libcutils/debugger.c +++ b/libcutils/debugger.c @@ -30,6 +30,7 @@ int dump_tombstone(pid_t tid, char* pathbuf, size_t pathlen) { debugger_msg_t msg; msg.tid = tid; msg.action = DEBUGGER_ACTION_DUMP_TOMBSTONE; + msg.abort_msg_address = 0; int result = 0; if (TEMP_FAILURE_RETRY(write(s, &msg, sizeof(msg))) != sizeof(msg)) { @@ -63,6 +64,7 @@ int dump_backtrace_to_file(pid_t tid, int fd) { debugger_msg_t msg; msg.tid = tid; msg.action = DEBUGGER_ACTION_DUMP_BACKTRACE; + msg.abort_msg_address = 0; int result = 0; if (TEMP_FAILURE_RETRY(write(s, &msg, sizeof(msg))) != sizeof(msg)) { diff --git a/libcutils/dir_hash.c b/libcutils/dir_hash.c index be14af6..098b5db 100644 --- a/libcutils/dir_hash.c +++ b/libcutils/dir_hash.c @@ -159,6 +159,7 @@ static int recurse(HashAlgorithm algorithm, const char *directory_path, free(name); free(node); + closedir(d); return -1; } diff --git a/libcutils/iosched_policy.c b/libcutils/iosched_policy.c index f350f58..5d90a01 100644 --- a/libcutils/iosched_policy.c +++ b/libcutils/iosched_policy.c @@ -1,7 +1,6 @@ - -/* libs/cutils/iosched_policy.c +/* ** -** Copyright 2007, The Android Open Source Project +** Copyright 2007-2014, 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. @@ -27,7 +26,11 @@ #include <cutils/iosched_policy.h> +#ifdef HAVE_ANDROID_OS +/* #include <linux/ioprio.h> */ extern int ioprio_set(int which, int who, int ioprio); +extern int ioprio_get(int which, int who); +#endif enum { WHO_PROCESS = 1, diff --git a/libcutils/socket_local_client.c b/libcutils/socket_local_client.c index 036ce2e..5310516 100644 --- a/libcutils/socket_local_client.c +++ b/libcutils/socket_local_client.c @@ -39,6 +39,8 @@ int socket_local_client(const char *name, int namespaceId, int type) #include "socket_local.h" +#define UNUSED __attribute__((unused)) + #define LISTEN_BACKLOG 4 /* Documented in header file. */ @@ -122,7 +124,7 @@ error: * Used by AndroidSocketImpl */ int socket_local_client_connect(int fd, const char *name, int namespaceId, - int type) + int type UNUSED) { struct sockaddr_un addr; socklen_t alen; diff --git a/libcutils/str_parms.c b/libcutils/str_parms.c index 9e1d2dc..7cfbcb3 100644 --- a/libcutils/str_parms.c +++ b/libcutils/str_parms.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Android Open Source Project + * Copyright (C) 2011-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. @@ -30,6 +30,8 @@ #include <cutils/str_parms.h> +#define UNUSED __attribute__((unused)) + struct str_parms { Hashmap *map; }; @@ -278,10 +280,11 @@ int str_parms_get_float(struct str_parms *str_parms, const char *key, return -ENOENT; out = strtof(value, &end); - if (*value != '\0' && *end == '\0') - return 0; + if (*value == '\0' || *end != '\0') + return -EINVAL; - return -EINVAL; + *val = out; + return 0; } static bool combine_strings(void *key, void *value, void *context) @@ -318,7 +321,7 @@ char *str_parms_to_str(struct str_parms *str_parms) return str; } -static bool dump_entry(void *key, void *value, void *context) +static bool dump_entry(void *key, void *value, void *context UNUSED) { ALOGI("key: '%s' value: '%s'\n", (char *)key, (char *)value); return true; diff --git a/libdiskconfig/config_mbr.c b/libdiskconfig/config_mbr.c index b89d382..7641b29 100644 --- a/libdiskconfig/config_mbr.c +++ b/libdiskconfig/config_mbr.c @@ -88,7 +88,7 @@ mk_pri_pentry(struct disk_info *dinfo, struct part_info *pinfo, int pnum, /* DO NOT DEREFERENCE */ struct pc_boot_record *mbr = (void *)PC_MBR_DISK_OFFSET; /* grab the offset in mbr where to write this partition entry. */ - item->offset = (loff_t)((uint32_t)((uint8_t *)(&mbr->ptable[pnum]))); + item->offset = (loff_t)((uintptr_t)((uint8_t *)(&mbr->ptable[pnum]))); } pentry = (struct pc_partition *) &item->data; diff --git a/libdiskconfig/diskconfig.c b/libdiskconfig/diskconfig.c index d5425de..6fd81b7 100644 --- a/libdiskconfig/diskconfig.c +++ b/libdiskconfig/diskconfig.c @@ -19,6 +19,7 @@ #include <errno.h> #include <fcntl.h> +#include <inttypes.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -80,7 +81,7 @@ parse_len(const char *str, uint64_t *plen) *plen *= multiple; if (*plen > 0xffffffffULL) { - ALOGE("Length specified is too large!: %llu KB", *plen); + ALOGE("Length specified is too large!: %"PRIu64" KB", *plen); return 1; } } @@ -371,8 +372,8 @@ validate(struct disk_info *dinfo) /* only matters for disks, not files */ if (S_ISBLK(stat.st_mode) && total_size > disk_size) { - ALOGE("Total requested size of partitions (%llu) is greater than disk " - "size (%llu).", total_size, disk_size); + ALOGE("Total requested size of partitions (%"PRIu64") is greater than disk " + "size (%"PRIu64").", total_size, disk_size); goto fail; } diff --git a/libion/Android.mk b/libion/Android.mk index 5121fee..e5d495b 100644 --- a/libion/Android.mk +++ b/libion/Android.mk @@ -5,11 +5,16 @@ LOCAL_SRC_FILES := ion.c LOCAL_MODULE := libion LOCAL_MODULE_TAGS := optional LOCAL_SHARED_LIBRARIES := liblog +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/kernel-headers +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include $(LOCAL_PATH)/kernel-headers include $(BUILD_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_SRC_FILES := ion.c ion_test.c LOCAL_MODULE := iontest LOCAL_MODULE_TAGS := optional tests +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/kernel-headers LOCAL_SHARED_LIBRARIES := liblog include $(BUILD_EXECUTABLE) + +include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/include/ion/ion.h b/libion/include/ion/ion.h index 018c0a1..f47793d 100644 --- a/include/ion/ion.h +++ b/libion/include/ion/ion.h @@ -21,22 +21,25 @@ #ifndef __SYS_CORE_ION_H #define __SYS_CORE_ION_H +#include <sys/types.h> #include <linux/ion.h> __BEGIN_DECLS +struct ion_handle; + int ion_open(); int ion_close(int fd); int ion_alloc(int fd, size_t len, size_t align, unsigned int heap_mask, - unsigned int flags, struct ion_handle **handle); + unsigned int flags, ion_user_handle_t *handle); int ion_alloc_fd(int fd, size_t len, size_t align, unsigned int heap_mask, - unsigned int flags, int *handle_fd); + 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 ion_free(int fd, ion_user_handle_t handle); +int ion_map(int fd, ion_user_handle_t handle, size_t length, int prot, int flags, off_t offset, unsigned char **ptr, int *map_fd); -int ion_share(int fd, struct ion_handle *handle, int *share_fd); -int ion_import(int fd, int share_fd, struct ion_handle **handle); +int ion_share(int fd, ion_user_handle_t handle, int *share_fd); +int ion_import(int fd, int share_fd, ion_user_handle_t *handle); __END_DECLS diff --git a/libion/ion.c b/libion/ion.c index 020c35b..80bdc2a 100644 --- a/libion/ion.c +++ b/libion/ion.c @@ -32,119 +32,139 @@ int ion_open() { - int fd = open("/dev/ion", O_RDWR); - if (fd < 0) - ALOGE("open /dev/ion failed!\n"); - return fd; + int fd = open("/dev/ion", O_RDWR); + if (fd < 0) + ALOGE("open /dev/ion failed!\n"); + return fd; } int ion_close(int fd) { - return close(fd); + int ret = close(fd); + if (ret < 0) + return -errno; + return ret; } static int ion_ioctl(int fd, int req, void *arg) { - int ret = ioctl(fd, req, arg); - if (ret < 0) { - ALOGE("ioctl %x failed with code %d: %s\n", req, - ret, strerror(errno)); - return -errno; - } - return ret; + int ret = ioctl(fd, req, arg); + if (ret < 0) { + ALOGE("ioctl %x failed with code %d: %s\n", req, + ret, strerror(errno)); + return -errno; + } + return ret; } int ion_alloc(int fd, size_t len, size_t align, unsigned int heap_mask, - unsigned int flags, struct ion_handle **handle) + unsigned int flags, ion_user_handle_t *handle) { - int ret; - struct ion_allocation_data data = { - .len = len, - .align = align, - .heap_mask = heap_mask, - .flags = flags, - }; - - ret = ion_ioctl(fd, ION_IOC_ALLOC, &data); - if (ret < 0) - return ret; - *handle = data.handle; + int ret; + struct ion_allocation_data data = { + .len = len, + .align = align, + .heap_id_mask = heap_mask, + .flags = flags, + }; + + if (handle == NULL) + return -EINVAL; + + ret = ion_ioctl(fd, ION_IOC_ALLOC, &data); + if (ret < 0) return ret; + *handle = data.handle; + return ret; } -int ion_free(int fd, struct ion_handle *handle) +int ion_free(int fd, ion_user_handle_t handle) { - struct ion_handle_data data = { - .handle = handle, - }; - return ion_ioctl(fd, ION_IOC_FREE, &data); + struct ion_handle_data data = { + .handle = handle, + }; + return ion_ioctl(fd, ION_IOC_FREE, &data); } -int ion_map(int fd, struct ion_handle *handle, size_t length, int prot, +int ion_map(int fd, ion_user_handle_t handle, size_t length, int prot, int flags, off_t offset, unsigned char **ptr, int *map_fd) { - struct ion_fd_data data = { - .handle = handle, - }; - - int ret = ion_ioctl(fd, ION_IOC_MAP, &data); - if (ret < 0) - return ret; - *map_fd = data.fd; - if (*map_fd < 0) { - ALOGE("map ioctl returned negative fd\n"); - return -EINVAL; - } - *ptr = mmap(NULL, length, prot, flags, *map_fd, offset); - if (*ptr == MAP_FAILED) { - ALOGE("mmap failed: %s\n", strerror(errno)); - return -errno; - } + int ret; + struct ion_fd_data data = { + .handle = handle, + }; + + if (map_fd == NULL) + return -EINVAL; + if (ptr == NULL) + return -EINVAL; + + ret = ion_ioctl(fd, ION_IOC_MAP, &data); + if (ret < 0) return ret; + *map_fd = data.fd; + if (*map_fd < 0) { + ALOGE("map ioctl returned negative fd\n"); + return -EINVAL; + } + *ptr = mmap(NULL, length, prot, flags, *map_fd, offset); + if (*ptr == MAP_FAILED) { + ALOGE("mmap failed: %s\n", strerror(errno)); + return -errno; + } + return ret; } -int ion_share(int fd, struct ion_handle *handle, int *share_fd) +int ion_share(int fd, ion_user_handle_t handle, int *share_fd) { - int map_fd; - struct ion_fd_data data = { - .handle = handle, - }; - - int ret = ion_ioctl(fd, ION_IOC_SHARE, &data); - if (ret < 0) - return ret; - *share_fd = data.fd; - if (*share_fd < 0) { - ALOGE("share ioctl returned negative fd\n"); - return -EINVAL; - } + int map_fd; + int ret; + struct ion_fd_data data = { + .handle = handle, + }; + + if (share_fd == NULL) + return -EINVAL; + + ret = ion_ioctl(fd, ION_IOC_SHARE, &data); + if (ret < 0) return ret; + *share_fd = data.fd; + if (*share_fd < 0) { + ALOGE("share ioctl returned negative fd\n"); + return -EINVAL; + } + 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; + unsigned int flags, int *handle_fd) { + ion_user_handle_t 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) +int ion_import(int fd, int share_fd, ion_user_handle_t *handle) { - struct ion_fd_data data = { - .fd = share_fd, - }; - - int ret = ion_ioctl(fd, ION_IOC_IMPORT, &data); - if (ret < 0) - return ret; - *handle = data.handle; + int ret; + struct ion_fd_data data = { + .fd = share_fd, + }; + + if (handle == NULL) + return -EINVAL; + + ret = ion_ioctl(fd, ION_IOC_IMPORT, &data); + if (ret < 0) return ret; + *handle = data.handle; + return ret; } int ion_sync_fd(int fd, int handle_fd) diff --git a/libion/ion_test.c b/libion/ion_test.c index 0caaa2a..8872282 100644 --- a/libion/ion_test.c +++ b/libion/ion_test.c @@ -1,3 +1,19 @@ +/* + * Copyright 2013 Google, Inc + * + * 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 <getopt.h> @@ -13,7 +29,6 @@ #include <ion/ion.h> #include <linux/ion.h> -#include <linux/omap_ion.h> size_t len = 1024*1024, align = 0; int prot = PROT_READ | PROT_WRITE; @@ -23,256 +38,250 @@ int heap_mask = 1; int test = -1; size_t stride; -int _ion_alloc_test(int *fd, struct ion_handle **handle) +int _ion_alloc_test(int *fd, ion_user_handle_t *handle) { - int ret; + int ret; - *fd = ion_open(); - if (*fd < 0) - return *fd; + *fd = ion_open(); + if (*fd < 0) + return *fd; - ret = ion_alloc(*fd, len, align, heap_mask, alloc_flags, handle); + ret = ion_alloc(*fd, len, align, heap_mask, alloc_flags, handle); - if (ret) - printf("%s failed: %s\n", __func__, strerror(ret)); - return ret; + if (ret) + printf("%s failed: %s\n", __func__, strerror(ret)); + return ret; } void ion_alloc_test() { - int fd, ret; - struct ion_handle *handle; + int fd, ret; + ion_user_handle_t handle; - if(_ion_alloc_test(&fd, &handle)) - return; + if(_ion_alloc_test(&fd, &handle)) + return; - ret = ion_free(fd, handle); - if (ret) { - printf("%s failed: %s %p\n", __func__, strerror(ret), handle); - return; - } - ion_close(fd); - printf("ion alloc test: passed\n"); + ret = ion_free(fd, handle); + if (ret) { + printf("%s failed: %s %d\n", __func__, strerror(ret), handle); + return; + } + ion_close(fd); + printf("ion alloc test: passed\n"); } void ion_map_test() { - int fd, map_fd, ret; - size_t i; - struct ion_handle *handle; - unsigned char *ptr; + int fd, map_fd, ret; + size_t i; + ion_user_handle_t handle; + unsigned char *ptr; - if(_ion_alloc_test(&fd, &handle)) - return; + if(_ion_alloc_test(&fd, &handle)) + return; - ret = ion_map(fd, handle, len, prot, map_flags, 0, &ptr, &map_fd); - if (ret) - return; + ret = ion_map(fd, handle, len, prot, map_flags, 0, &ptr, &map_fd); + if (ret) + return; - for (i = 0; i < len; i++) { - ptr[i] = (unsigned char)i; - } - for (i = 0; i < len; i++) - if (ptr[i] != (unsigned char)i) - printf("%s failed wrote %d read %d from mapped " - "memory\n", __func__, i, ptr[i]); - /* clean up properly */ - ret = ion_free(fd, handle); - ion_close(fd); - munmap(ptr, len); - close(map_fd); + for (i = 0; i < len; i++) { + ptr[i] = (unsigned char)i; + } + for (i = 0; i < len; i++) + if (ptr[i] != (unsigned char)i) + printf("%s failed wrote %zu read %d from mapped " + "memory\n", __func__, i, ptr[i]); + /* clean up properly */ + ret = ion_free(fd, handle); + ion_close(fd); + munmap(ptr, len); + close(map_fd); - _ion_alloc_test(&fd, &handle); - close(fd); + _ion_alloc_test(&fd, &handle); + close(fd); #if 0 - munmap(ptr, len); - close(map_fd); - ion_close(fd); + munmap(ptr, len); + close(map_fd); + ion_close(fd); - _ion_alloc_test(len, align, flags, &fd, &handle); - close(map_fd); - ret = ion_map(fd, handle, len, prot, flags, 0, &ptr, &map_fd); - /* don't clean up */ + _ion_alloc_test(len, align, flags, &fd, &handle); + close(map_fd); + ret = ion_map(fd, handle, len, prot, flags, 0, &ptr, &map_fd); + /* don't clean up */ #endif } void ion_share_test() { - struct ion_handle *handle; - int sd[2]; - int num_fd = 1; - struct iovec count_vec = { - .iov_base = &num_fd, - .iov_len = sizeof num_fd, - }; - char buf[CMSG_SPACE(sizeof(int))]; - socketpair(AF_UNIX, SOCK_STREAM, 0, sd); - if (fork()) { - struct msghdr msg = { - .msg_control = buf, - .msg_controllen = sizeof buf, - .msg_iov = &count_vec, - .msg_iovlen = 1, - }; + ion_user_handle_t handle; + int sd[2]; + int num_fd = 1; + struct iovec count_vec = { + .iov_base = &num_fd, + .iov_len = sizeof num_fd, + }; + char buf[CMSG_SPACE(sizeof(int))]; + socketpair(AF_UNIX, SOCK_STREAM, 0, sd); + if (fork()) { + struct msghdr msg = { + .msg_control = buf, + .msg_controllen = sizeof buf, + .msg_iov = &count_vec, + .msg_iovlen = 1, + }; - struct cmsghdr *cmsg; - int fd, share_fd, ret; - char *ptr; - /* parent */ - if(_ion_alloc_test(&fd, &handle)) - return; - ret = ion_share(fd, handle, &share_fd); - if (ret) - printf("share failed %s\n", strerror(errno)); - ptr = mmap(NULL, len, prot, map_flags, share_fd, 0); - if (ptr == MAP_FAILED) { - return; - } - strcpy(ptr, "master"); - cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = CMSG_LEN(sizeof(int)); - *(int *)CMSG_DATA(cmsg) = share_fd; - /* send the fd */ - printf("master? [%10s] should be [master]\n", ptr); - printf("master sending msg 1\n"); - sendmsg(sd[0], &msg, 0); - if (recvmsg(sd[0], &msg, 0) < 0) - perror("master recv msg 2"); - printf("master? [%10s] should be [child]\n", ptr); + struct cmsghdr *cmsg; + int fd, share_fd, ret; + char *ptr; + /* parent */ + if(_ion_alloc_test(&fd, &handle)) + return; + ret = ion_share(fd, handle, &share_fd); + if (ret) + printf("share failed %s\n", strerror(errno)); + ptr = mmap(NULL, len, prot, map_flags, share_fd, 0); + if (ptr == MAP_FAILED) { + return; + } + strcpy(ptr, "master"); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + *(int *)CMSG_DATA(cmsg) = share_fd; + /* send the fd */ + printf("master? [%10s] should be [master]\n", ptr); + printf("master sending msg 1\n"); + sendmsg(sd[0], &msg, 0); + if (recvmsg(sd[0], &msg, 0) < 0) + perror("master recv msg 2"); + printf("master? [%10s] should be [child]\n", ptr); - /* send ping */ - sendmsg(sd[0], &msg, 0); - printf("master->master? [%10s]\n", ptr); - if (recvmsg(sd[0], &msg, 0) < 0) - perror("master recv 1"); - } else { - struct msghdr msg; - struct cmsghdr *cmsg; - char* ptr; - int fd, recv_fd; - char* child_buf[100]; - /* child */ - struct iovec count_vec = { - .iov_base = child_buf, - .iov_len = sizeof child_buf, - }; + /* send ping */ + sendmsg(sd[0], &msg, 0); + printf("master->master? [%10s]\n", ptr); + if (recvmsg(sd[0], &msg, 0) < 0) + perror("master recv 1"); + } else { + struct msghdr msg; + struct cmsghdr *cmsg; + char* ptr; + int fd, recv_fd; + char* child_buf[100]; + /* child */ + struct iovec count_vec = { + .iov_base = child_buf, + .iov_len = sizeof child_buf, + }; - struct msghdr child_msg = { - .msg_control = buf, - .msg_controllen = sizeof buf, - .msg_iov = &count_vec, - .msg_iovlen = 1, - }; + struct msghdr child_msg = { + .msg_control = buf, + .msg_controllen = sizeof buf, + .msg_iov = &count_vec, + .msg_iovlen = 1, + }; - if (recvmsg(sd[1], &child_msg, 0) < 0) - perror("child recv msg 1"); - cmsg = CMSG_FIRSTHDR(&child_msg); - if (cmsg == NULL) { - printf("no cmsg rcvd in child"); - return; - } - recv_fd = *(int*)CMSG_DATA(cmsg); - if (recv_fd < 0) { - printf("could not get recv_fd from socket"); - return; - } - printf("child %d\n", recv_fd); - fd = ion_open(); - ptr = mmap(NULL, len, prot, map_flags, recv_fd, 0); - if (ptr == MAP_FAILED) { - return; - } - printf("child? [%10s] should be [master]\n", ptr); - strcpy(ptr, "child"); - printf("child sending msg 2\n"); - sendmsg(sd[1], &child_msg, 0); - } + if (recvmsg(sd[1], &child_msg, 0) < 0) + perror("child recv msg 1"); + cmsg = CMSG_FIRSTHDR(&child_msg); + if (cmsg == NULL) { + printf("no cmsg rcvd in child"); + return; + } + recv_fd = *(int*)CMSG_DATA(cmsg); + if (recv_fd < 0) { + printf("could not get recv_fd from socket"); + return; + } + printf("child %d\n", recv_fd); + fd = ion_open(); + ptr = mmap(NULL, len, prot, map_flags, recv_fd, 0); + if (ptr == MAP_FAILED) { + return; + } + printf("child? [%10s] should be [master]\n", ptr); + strcpy(ptr, "child"); + printf("child sending msg 2\n"); + sendmsg(sd[1], &child_msg, 0); + } } int main(int argc, char* argv[]) { - int c; - enum tests { - ALLOC_TEST = 0, MAP_TEST, SHARE_TEST, - }; + int c; + enum tests { + ALLOC_TEST = 0, MAP_TEST, SHARE_TEST, + }; - while (1) { - 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'}, - }; - int i = 0; - c = getopt_long(argc, argv, "af:h:l:mr:st", opts, &i); - if (c == -1) - break; + while (1) { + 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'}, + }; + int i = 0; + c = getopt_long(argc, argv, "af:h:l:mr:st", opts, &i); + if (c == -1) + break; - switch (c) { - case 'l': - len = atol(optarg); - break; - case 'g': - align = atol(optarg); - break; - case 'z': - map_flags = 0; - map_flags |= strstr(optarg, "PROT_EXEC") ? - PROT_EXEC : 0; - map_flags |= strstr(optarg, "PROT_READ") ? - PROT_READ: 0; - map_flags |= strstr(optarg, "PROT_WRITE") ? - PROT_WRITE: 0; - map_flags |= strstr(optarg, "PROT_NONE") ? - PROT_NONE: 0; - break; - case 'p': - prot = 0; - prot |= strstr(optarg, "MAP_PRIVATE") ? - MAP_PRIVATE : 0; - prot |= strstr(optarg, "MAP_SHARED") ? - MAP_PRIVATE : 0; - break; - case 'f': - alloc_flags = atol(optarg); - break; - case 'h': - heap_mask = atol(optarg); - break; - case 'a': - test = ALLOC_TEST; - break; - case 'm': - test = MAP_TEST; - break; - case 's': - test = SHARE_TEST; - break; - } - } - 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(); - break; - case MAP_TEST: - ion_map_test(); - break; - case SHARE_TEST: - ion_share_test(); - break; - default: - printf("must specify a test (alloc, map, share)\n"); - } - return 0; + switch (c) { + case 'l': + len = atol(optarg); + break; + case 'g': + align = atol(optarg); + break; + case 'z': + map_flags = 0; + map_flags |= strstr(optarg, "PROT_EXEC") ? PROT_EXEC : 0; + map_flags |= strstr(optarg, "PROT_READ") ? PROT_READ: 0; + map_flags |= strstr(optarg, "PROT_WRITE") ? PROT_WRITE: 0; + map_flags |= strstr(optarg, "PROT_NONE") ? PROT_NONE: 0; + break; + case 'p': + prot = 0; + prot |= strstr(optarg, "MAP_PRIVATE") ? MAP_PRIVATE : 0; + prot |= strstr(optarg, "MAP_SHARED") ? MAP_PRIVATE : 0; + break; + case 'f': + alloc_flags = atol(optarg); + break; + case 'h': + heap_mask = atol(optarg); + break; + case 'a': + test = ALLOC_TEST; + break; + case 'm': + test = MAP_TEST; + break; + case 's': + test = SHARE_TEST; + break; + } + } + printf("test %d, len %zu, align %zu, 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(); + break; + case MAP_TEST: + ion_map_test(); + break; + case SHARE_TEST: + ion_share_test(); + break; + default: + printf("must specify a test (alloc, map, share)\n"); + } + return 0; } diff --git a/libion/kernel-headers/linux/ion.h b/libion/kernel-headers/linux/ion.h new file mode 100644 index 0000000..5af39d0 --- /dev/null +++ b/libion/kernel-headers/linux/ion.h @@ -0,0 +1,78 @@ +/**************************************************************************** + **************************************************************************** + *** + *** This header was automatically generated from a Linux kernel header + *** of the same name, to make information necessary for userspace to + *** call into the kernel available to libc. It contains only constants, + *** structures, and macros generated from the original header, and thus, + *** contains no copyrightable information. + *** + *** To edit the content of this header, modify the corresponding + *** source file (e.g. under external/kernel-headers/original/) then + *** run bionic/libc/kernel/tools/update_all.py + *** + *** Any manual change here will be lost the next time this script will + *** be run. You've been warned! + *** + **************************************************************************** + ****************************************************************************/ +#ifndef _UAPI_LINUX_ION_H +#define _UAPI_LINUX_ION_H +#include <linux/ioctl.h> +#include <linux/types.h> +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +typedef int ion_user_handle_t; +enum ion_heap_type { + ION_HEAP_TYPE_SYSTEM, + ION_HEAP_TYPE_SYSTEM_CONTIG, +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + ION_HEAP_TYPE_CARVEOUT, + ION_HEAP_TYPE_CHUNK, + ION_HEAP_TYPE_DMA, + ION_HEAP_TYPE_CUSTOM, +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + ION_NUM_HEAPS = 16, +}; +#define ION_HEAP_SYSTEM_MASK (1 << ION_HEAP_TYPE_SYSTEM) +#define ION_HEAP_SYSTEM_CONTIG_MASK (1 << ION_HEAP_TYPE_SYSTEM_CONTIG) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define ION_HEAP_CARVEOUT_MASK (1 << ION_HEAP_TYPE_CARVEOUT) +#define ION_HEAP_TYPE_DMA_MASK (1 << ION_HEAP_TYPE_DMA) +#define ION_NUM_HEAP_IDS sizeof(unsigned int) * 8 +#define ION_FLAG_CACHED 1 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define ION_FLAG_CACHED_NEEDS_SYNC 2 +struct ion_allocation_data { + size_t len; + size_t align; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + unsigned int heap_id_mask; + unsigned int flags; + ion_user_handle_t handle; +}; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +struct ion_fd_data { + ion_user_handle_t handle; + int fd; +}; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +struct ion_handle_data { + ion_user_handle_t handle; +}; +struct ion_custom_data { +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + unsigned int cmd; + unsigned long arg; +}; +#define ION_IOC_MAGIC 'I' +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define ION_IOC_ALLOC _IOWR(ION_IOC_MAGIC, 0, struct ion_allocation_data) +#define ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data) +#define ION_IOC_MAP _IOWR(ION_IOC_MAGIC, 2, struct ion_fd_data) +#define ION_IOC_SHARE _IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define ION_IOC_IMPORT _IOWR(ION_IOC_MAGIC, 5, struct ion_fd_data) +#define ION_IOC_SYNC _IOWR(ION_IOC_MAGIC, 7, struct ion_fd_data) +#define ION_IOC_CUSTOM _IOWR(ION_IOC_MAGIC, 6, struct ion_custom_data) +#endif +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ diff --git a/libion/kernel-headers/linux/ion_test.h b/libion/kernel-headers/linux/ion_test.h new file mode 100644 index 0000000..6f3e2a7 --- /dev/null +++ b/libion/kernel-headers/linux/ion_test.h @@ -0,0 +1,38 @@ +/**************************************************************************** + **************************************************************************** + *** + *** This header was automatically generated from a Linux kernel header + *** of the same name, to make information necessary for userspace to + *** call into the kernel available to libc. It contains only constants, + *** structures, and macros generated from the original header, and thus, + *** contains no copyrightable information. + *** + *** To edit the content of this header, modify the corresponding + *** source file (e.g. under external/kernel-headers/original/) then + *** run bionic/libc/kernel/tools/update_all.py + *** + *** Any manual change here will be lost the next time this script will + *** be run. You've been warned! + *** + **************************************************************************** + ****************************************************************************/ +#ifndef _UAPI_LINUX_ION_TEST_H +#define _UAPI_LINUX_ION_TEST_H +#include <linux/ioctl.h> +#include <linux/types.h> +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +struct ion_test_rw_data { + __u64 ptr; + __u64 offset; + __u64 size; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + int write; + int __padding; +}; +#define ION_IOC_MAGIC 'I' +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define ION_IOC_TEST_SET_FD _IO(ION_IOC_MAGIC, 0xf0) +#define ION_IOC_TEST_DMA_MAPPING _IOW(ION_IOC_MAGIC, 0xf1, struct ion_test_rw_data) +#define ION_IOC_TEST_KERNEL_MAPPING _IOW(ION_IOC_MAGIC, 0xf2, struct ion_test_rw_data) +#endif +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ diff --git a/libion/original-kernel-headers/linux/ion.h b/libion/original-kernel-headers/linux/ion.h new file mode 100644 index 0000000..f09e7c1 --- /dev/null +++ b/libion/original-kernel-headers/linux/ion.h @@ -0,0 +1,196 @@ +/* + * drivers/staging/android/uapi/ion.h + * + * Copyright (C) 2011 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _UAPI_LINUX_ION_H +#define _UAPI_LINUX_ION_H + +#include <linux/ioctl.h> +#include <linux/types.h> + +typedef int ion_user_handle_t; + +/** + * enum ion_heap_types - list of all possible types of heaps + * @ION_HEAP_TYPE_SYSTEM: memory allocated via vmalloc + * @ION_HEAP_TYPE_SYSTEM_CONTIG: memory allocated via kmalloc + * @ION_HEAP_TYPE_CARVEOUT: memory allocated from a prereserved + * carveout heap, allocations are physically + * contiguous + * @ION_HEAP_TYPE_DMA: memory allocated via DMA API + * @ION_NUM_HEAPS: helper for iterating over heaps, a bit mask + * is used to identify the heaps, so only 32 + * total heap types are supported + */ +enum ion_heap_type { + ION_HEAP_TYPE_SYSTEM, + ION_HEAP_TYPE_SYSTEM_CONTIG, + ION_HEAP_TYPE_CARVEOUT, + ION_HEAP_TYPE_CHUNK, + ION_HEAP_TYPE_DMA, + ION_HEAP_TYPE_CUSTOM, /* must be last so device specific heaps always + are at the end of this enum */ + ION_NUM_HEAPS = 16, +}; + +#define ION_HEAP_SYSTEM_MASK (1 << ION_HEAP_TYPE_SYSTEM) +#define ION_HEAP_SYSTEM_CONTIG_MASK (1 << ION_HEAP_TYPE_SYSTEM_CONTIG) +#define ION_HEAP_CARVEOUT_MASK (1 << ION_HEAP_TYPE_CARVEOUT) +#define ION_HEAP_TYPE_DMA_MASK (1 << ION_HEAP_TYPE_DMA) + +#define ION_NUM_HEAP_IDS sizeof(unsigned int) * 8 + +/** + * allocation flags - the lower 16 bits are used by core ion, the upper 16 + * bits are reserved for use by the heaps themselves. + */ +#define ION_FLAG_CACHED 1 /* mappings of this buffer should be + cached, ion will do cache + maintenance when the buffer is + mapped for dma */ +#define ION_FLAG_CACHED_NEEDS_SYNC 2 /* mappings of this buffer will created + at mmap time, if this is set + caches must be managed manually */ + +/** + * DOC: Ion Userspace API + * + * create a client by opening /dev/ion + * most operations handled via following ioctls + * + */ + +/** + * struct ion_allocation_data - metadata passed from userspace for allocations + * @len: size of the allocation + * @align: required alignment of the allocation + * @heap_id_mask: mask of heap ids to allocate from + * @flags: flags passed to heap + * @handle: pointer that will be populated with a cookie to use to + * refer to this allocation + * + * Provided by userspace as an argument to the ioctl + */ +struct ion_allocation_data { + size_t len; + size_t align; + unsigned int heap_id_mask; + unsigned int flags; + ion_user_handle_t handle; +}; + +/** + * struct ion_fd_data - metadata passed to/from userspace for a handle/fd pair + * @handle: a handle + * @fd: a file descriptor representing that handle + * + * For ION_IOC_SHARE or ION_IOC_MAP userspace populates the handle field with + * the handle returned from ion alloc, and the kernel returns the file + * descriptor to share or map in the fd field. For ION_IOC_IMPORT, userspace + * provides the file descriptor and the kernel returns the handle. + */ +struct ion_fd_data { + ion_user_handle_t handle; + int fd; +}; + +/** + * struct ion_handle_data - a handle passed to/from the kernel + * @handle: a handle + */ +struct ion_handle_data { + ion_user_handle_t handle; +}; + +/** + * struct ion_custom_data - metadata passed to/from userspace for a custom ioctl + * @cmd: the custom ioctl function to call + * @arg: additional data to pass to the custom ioctl, typically a user + * pointer to a predefined structure + * + * This works just like the regular cmd and arg fields of an ioctl. + */ +struct ion_custom_data { + unsigned int cmd; + unsigned long arg; +}; + +#define ION_IOC_MAGIC 'I' + +/** + * DOC: ION_IOC_ALLOC - allocate memory + * + * Takes an ion_allocation_data struct and returns it with the handle field + * populated with the opaque handle for the allocation. + */ +#define ION_IOC_ALLOC _IOWR(ION_IOC_MAGIC, 0, \ + struct ion_allocation_data) + +/** + * DOC: ION_IOC_FREE - free memory + * + * Takes an ion_handle_data struct and frees the handle. + */ +#define ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data) + +/** + * DOC: ION_IOC_MAP - get a file descriptor to mmap + * + * Takes an ion_fd_data struct with the handle field populated with a valid + * opaque handle. Returns the struct with the fd field set to a file + * descriptor open in the current address space. This file descriptor + * can then be used as an argument to mmap. + */ +#define ION_IOC_MAP _IOWR(ION_IOC_MAGIC, 2, struct ion_fd_data) + +/** + * DOC: ION_IOC_SHARE - creates a file descriptor to use to share an allocation + * + * Takes an ion_fd_data struct with the handle field populated with a valid + * opaque handle. Returns the struct with the fd field set to a file + * descriptor open in the current address space. This file descriptor + * can then be passed to another process. The corresponding opaque handle can + * be retrieved via ION_IOC_IMPORT. + */ +#define ION_IOC_SHARE _IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data) + +/** + * DOC: ION_IOC_IMPORT - imports a shared file descriptor + * + * Takes an ion_fd_data struct with the fd field populated with a valid file + * descriptor obtained from ION_IOC_SHARE and returns the struct with the handle + * filed set to the corresponding opaque handle. + */ +#define ION_IOC_IMPORT _IOWR(ION_IOC_MAGIC, 5, struct ion_fd_data) + +/** + * DOC: ION_IOC_SYNC - syncs a shared file descriptors to memory + * + * Deprecated in favor of using the dma_buf api's correctly (syncing + * will happend automatically when the buffer is mapped to a device). + * If necessary should be used after touching a cached buffer from the cpu, + * this will make the buffer in memory coherent. + */ +#define ION_IOC_SYNC _IOWR(ION_IOC_MAGIC, 7, struct ion_fd_data) + +/** + * DOC: ION_IOC_CUSTOM - call architecture specific ion ioctl + * + * Takes the argument of the architecture specific ioctl to call and + * passes appropriate userdata for that ioctl + */ +#define ION_IOC_CUSTOM _IOWR(ION_IOC_MAGIC, 6, struct ion_custom_data) + +#endif /* _UAPI_LINUX_ION_H */ diff --git a/libion/original-kernel-headers/linux/ion_test.h b/libion/original-kernel-headers/linux/ion_test.h new file mode 100644 index 0000000..ffef06f --- /dev/null +++ b/libion/original-kernel-headers/linux/ion_test.h @@ -0,0 +1,70 @@ +/* + * drivers/staging/android/uapi/ion.h + * + * Copyright (C) 2011 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _UAPI_LINUX_ION_TEST_H +#define _UAPI_LINUX_ION_TEST_H + +#include <linux/ioctl.h> +#include <linux/types.h> + +/** + * struct ion_test_rw_data - metadata passed to the kernel to read handle + * @ptr: a pointer to an area at least as large as size + * @offset: offset into the ion buffer to start reading + * @size: size to read or write + * @write: 1 to write, 0 to read + */ +struct ion_test_rw_data { + __u64 ptr; + __u64 offset; + __u64 size; + int write; + int __padding; +}; + +#define ION_IOC_MAGIC 'I' + +/** + * DOC: ION_IOC_TEST_SET_DMA_BUF - attach a dma buf to the test driver + * + * Attaches a dma buf fd to the test driver. Passing a second fd or -1 will + * release the first fd. + */ +#define ION_IOC_TEST_SET_FD \ + _IO(ION_IOC_MAGIC, 0xf0) + +/** + * DOC: ION_IOC_TEST_DMA_MAPPING - read or write memory from a handle as DMA + * + * Reads or writes the memory from a handle using an uncached mapping. Can be + * used by unit tests to emulate a DMA engine as close as possible. Only + * expected to be used for debugging and testing, may not always be available. + */ +#define ION_IOC_TEST_DMA_MAPPING \ + _IOW(ION_IOC_MAGIC, 0xf1, struct ion_test_rw_data) + +/** + * DOC: ION_IOC_TEST_KERNEL_MAPPING - read or write memory from a handle + * + * Reads or writes the memory from a handle using a kernel mapping. Can be + * used by unit tests to test heap map_kernel functions. Only expected to be + * used for debugging and testing, may not always be available. + */ +#define ION_IOC_TEST_KERNEL_MAPPING \ + _IOW(ION_IOC_MAGIC, 0xf2, struct ion_test_rw_data) + + +#endif /* _UAPI_LINUX_ION_H */ diff --git a/libion/tests/Android.mk b/libion/tests/Android.mk new file mode 100644 index 0000000..8dc7f9d --- /dev/null +++ b/libion/tests/Android.mk @@ -0,0 +1,34 @@ +# +# 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. +# + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := ion-unit-tests +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk +LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers +LOCAL_SHARED_LIBRARIES += libion +LOCAL_STATIC_LIBRARIES += libgtest_main +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../kernel-headers +LOCAL_SRC_FILES := \ + ion_test_fixture.cpp \ + allocate_test.cpp \ + formerly_valid_handle_test.cpp \ + invalid_values_test.cpp \ + map_test.cpp \ + device_test.cpp \ + exit_test.cpp +include $(BUILD_NATIVE_TEST) diff --git a/libion/tests/allocate_test.cpp b/libion/tests/allocate_test.cpp new file mode 100644 index 0000000..e26b302 --- /dev/null +++ b/libion/tests/allocate_test.cpp @@ -0,0 +1,145 @@ +/* + * 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. + */ + +#include <sys/mman.h> + +#include <gtest/gtest.h> + +#include <ion/ion.h> +#include "ion_test_fixture.h" + +class Allocate : public IonAllHeapsTest { +}; + +TEST_F(Allocate, Allocate) +{ + static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024}; + for (unsigned int heapMask : m_allHeaps) { + for (size_t size : allocationSizes) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + SCOPED_TRACE(::testing::Message() << "size " << size); + ion_user_handle_t handle = 0; + ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, 0, &handle)); + ASSERT_TRUE(handle != 0); + ASSERT_EQ(0, ion_free(m_ionFd, handle)); + } + } +} + +TEST_F(Allocate, AllocateCached) +{ + static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024}; + for (unsigned int heapMask : m_allHeaps) { + for (size_t size : allocationSizes) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + SCOPED_TRACE(::testing::Message() << "size " << size); + ion_user_handle_t handle = 0; + ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED, &handle)); + ASSERT_TRUE(handle != 0); + ASSERT_EQ(0, ion_free(m_ionFd, handle)); + } + } +} + +TEST_F(Allocate, AllocateCachedNeedsSync) +{ + static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024}; + for (unsigned int heapMask : m_allHeaps) { + for (size_t size : allocationSizes) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + SCOPED_TRACE(::testing::Message() << "size " << size); + ion_user_handle_t handle = 0; + ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED_NEEDS_SYNC, &handle)); + ASSERT_TRUE(handle != 0); + ASSERT_EQ(0, ion_free(m_ionFd, handle)); + } + } +} + +TEST_F(Allocate, RepeatedAllocate) +{ + static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024}; + for (unsigned int heapMask : m_allHeaps) { + for (size_t size : allocationSizes) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + SCOPED_TRACE(::testing::Message() << "size " << size); + ion_user_handle_t handle = 0; + + for (unsigned int i = 0; i < 1024; i++) { + SCOPED_TRACE(::testing::Message() << "iteration " << i); + ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, 0, &handle)); + ASSERT_TRUE(handle != 0); + ASSERT_EQ(0, ion_free(m_ionFd, handle)); + } + } + } +} + +TEST_F(Allocate, Zeroed) +{ + void *zeroes = calloc(4096, 1); + + for (unsigned int heapMask : m_allHeaps) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + int fds[16]; + for (unsigned int i = 0; i < 16; i++) { + int map_fd = -1; + + ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, 0, &map_fd)); + ASSERT_GE(map_fd, 0); + + void *ptr = NULL; + ptr = mmap(NULL, 4096, PROT_WRITE, MAP_SHARED, map_fd, 0); + ASSERT_TRUE(ptr != NULL); + + memset(ptr, 0xaa, 4096); + + ASSERT_EQ(0, munmap(ptr, 4096)); + fds[i] = map_fd; + } + + for (unsigned int i = 0; i < 16; i++) { + ASSERT_EQ(0, close(fds[i])); + } + + int newIonFd = ion_open(); + int map_fd = -1; + + ASSERT_EQ(0, ion_alloc_fd(newIonFd, 4096, 0, heapMask, 0, &map_fd)); + ASSERT_GE(map_fd, 0); + + void *ptr = NULL; + ptr = mmap(NULL, 4096, PROT_READ, MAP_SHARED, map_fd, 0); + ASSERT_TRUE(ptr != NULL); + + ASSERT_EQ(0, memcmp(ptr, zeroes, 4096)); + + ASSERT_EQ(0, munmap(ptr, 4096)); + ASSERT_EQ(0, close(map_fd)); + } + + free(zeroes); + +} + +TEST_F(Allocate, Large) +{ + for (unsigned int heapMask : m_allHeaps) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + ion_user_handle_t handle = 0; + ASSERT_EQ(-ENOMEM, ion_alloc(m_ionFd, 3UL*1024*1024*1024, 0, heapMask, 0, &handle)); + } +} diff --git a/libion/tests/device_test.cpp b/libion/tests/device_test.cpp new file mode 100644 index 0000000..6f6e1bd --- /dev/null +++ b/libion/tests/device_test.cpp @@ -0,0 +1,568 @@ +/* + * 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. + */ + +#include <fcntl.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <linux/ion_test.h> + +#include <gtest/gtest.h> + +#include <ion/ion.h> + +#include "ion_test_fixture.h" + +#define ALIGN(x,y) (((x) + ((y) - 1)) & ~((y) - 1)) + +class Device : public IonAllHeapsTest { + public: + virtual void SetUp(); + virtual void TearDown(); + int m_deviceFd; + void readDMA(int fd, void *buf, size_t size); + void writeDMA(int fd, void *buf, size_t size); + void readKernel(int fd, void *buf, size_t size); + void writeKernel(int fd, void *buf, size_t size); + void blowCache(); + void dirtyCache(void *ptr, size_t size); +}; + +void Device::SetUp() +{ + IonAllHeapsTest::SetUp(); + m_deviceFd = open("/dev/ion-test", O_RDWR); + ASSERT_GE(m_deviceFd, 0); +} + +void Device::TearDown() +{ + ASSERT_EQ(0, close(m_deviceFd)); + IonAllHeapsTest::TearDown(); +} + +void Device::readDMA(int fd, void *buf, size_t size) +{ + ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, fd)); + struct ion_test_rw_data ion_test_rw_data = { + .ptr = (uint64_t)buf, + .offset = 0, + .size = size, + .write = 0, + }; + + ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_DMA_MAPPING, &ion_test_rw_data)); + ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, -1)); +} + +void Device::writeDMA(int fd, void *buf, size_t size) +{ + ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, fd)); + struct ion_test_rw_data ion_test_rw_data = { + .ptr = (uint64_t)buf, + .offset = 0, + .size = size, + .write = 1, + }; + + ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_DMA_MAPPING, &ion_test_rw_data)); + ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, -1)); +} + +void Device::readKernel(int fd, void *buf, size_t size) +{ + ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, fd)); + struct ion_test_rw_data ion_test_rw_data = { + .ptr = (uint64_t)buf, + .offset = 0, + .size = size, + .write = 0, + }; + + ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_KERNEL_MAPPING, &ion_test_rw_data)); + ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, -1)); +} + +void Device::writeKernel(int fd, void *buf, size_t size) +{ + ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, fd)); + struct ion_test_rw_data ion_test_rw_data = { + .ptr = (uint64_t)buf, + .offset = 0, + .size = size, + .write = 1, + }; + + ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_KERNEL_MAPPING, &ion_test_rw_data)); + ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, -1)); +} + +void Device::blowCache() +{ + const size_t bigger_than_cache = 8*1024*1024; + void *buf1 = malloc(bigger_than_cache); + void *buf2 = malloc(bigger_than_cache); + memset(buf1, 0xaa, bigger_than_cache); + memcpy(buf2, buf1, bigger_than_cache); + free(buf1); + free(buf2); +} + +void Device::dirtyCache(void *ptr, size_t size) +{ + /* try to dirty cache lines */ + for (size_t i = size-1; i > 0; i--) { + ((volatile char *)ptr)[i]; + ((char *)ptr)[i] = i; + } +} + +TEST_F(Device, KernelReadCached) +{ + void *alloc = malloc(8192 + 1024); + void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024); + + for (unsigned int heapMask : m_allHeaps) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + int map_fd = -1; + unsigned int flags = ION_FLAG_CACHED; + + ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd)); + ASSERT_GE(map_fd, 0); + + void *ptr; + ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0); + ASSERT_TRUE(ptr != NULL); + + for (int i = 0; i < 4096; i++) + ((char *)ptr)[i] = i; + + ((char*)buf)[4096] = 0x12; + readKernel(map_fd, buf, 4096); + ASSERT_EQ(((char*)buf)[4096], 0x12); + + for (int i = 0; i < 4096; i++) + ASSERT_EQ((char)i, ((char *)buf)[i]); + + ASSERT_EQ(0, munmap(ptr, 4096)); + ASSERT_EQ(0, close(map_fd)); + } + + free(alloc); +} + +TEST_F(Device, KernelWriteCached) +{ + void *alloc = malloc(8192 + 1024); + void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024); + + for (int i = 0; i < 4096; i++) + ((char *)buf)[i] = i; + + for (unsigned int heapMask : m_allHeaps) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + int map_fd = -1; + unsigned int flags = ION_FLAG_CACHED; + + ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd)); + ASSERT_GE(map_fd, 0); + + void *ptr; + ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0); + ASSERT_TRUE(ptr != NULL); + + dirtyCache(ptr, 4096); + + writeKernel(map_fd, buf, 4096); + + for (int i = 0; i < 4096; i++) + ASSERT_EQ((char)i, ((char *)ptr)[i]) << i; + + ASSERT_EQ(0, munmap(ptr, 4096)); + ASSERT_EQ(0, close(map_fd)); + } + + free(alloc); +} + +TEST_F(Device, DMAReadCached) +{ + void *alloc = malloc(8192 + 1024); + void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024); + + for (unsigned int heapMask : m_allHeaps) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + int map_fd = -1; + unsigned int flags = ION_FLAG_CACHED; + + ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd)); + ASSERT_GE(map_fd, 0); + + void *ptr; + ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0); + ASSERT_TRUE(ptr != NULL); + + for (int i = 0; i < 4096; i++) + ((char *)ptr)[i] = i; + + readDMA(map_fd, buf, 4096); + + for (int i = 0; i < 4096; i++) + ASSERT_EQ((char)i, ((char *)buf)[i]); + + ASSERT_EQ(0, munmap(ptr, 4096)); + ASSERT_EQ(0, close(map_fd)); + } + + free(alloc); +} + +TEST_F(Device, DMAWriteCached) +{ + void *alloc = malloc(8192 + 1024); + void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024); + + for (int i = 0; i < 4096; i++) + ((char *)buf)[i] = i; + + for (unsigned int heapMask : m_allHeaps) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + int map_fd = -1; + unsigned int flags = ION_FLAG_CACHED; + + ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd)); + ASSERT_GE(map_fd, 0); + + void *ptr; + ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0); + ASSERT_TRUE(ptr != NULL); + + dirtyCache(ptr, 4096); + + writeDMA(map_fd, buf, 4096); + + for (int i = 0; i < 4096; i++) + ASSERT_EQ((char)i, ((char *)ptr)[i]) << i; + + ASSERT_EQ(0, munmap(ptr, 4096)); + ASSERT_EQ(0, close(map_fd)); + } + + free(alloc); +} + +TEST_F(Device, KernelReadCachedNeedsSync) +{ + void *alloc = malloc(8192 + 1024); + void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024); + + for (unsigned int heapMask : m_allHeaps) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + int map_fd = -1; + unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC; + + ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd)); + ASSERT_GE(map_fd, 0); + + void *ptr; + ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0); + ASSERT_TRUE(ptr != NULL); + + for (int i = 0; i < 4096; i++) + ((char *)ptr)[i] = i; + + ((char*)buf)[4096] = 0x12; + readKernel(map_fd, buf, 4096); + ASSERT_EQ(((char*)buf)[4096], 0x12); + + for (int i = 0; i < 4096; i++) + ASSERT_EQ((char)i, ((char *)buf)[i]); + + ASSERT_EQ(0, munmap(ptr, 4096)); + ASSERT_EQ(0, close(map_fd)); + } + + free(alloc); +} + +TEST_F(Device, KernelWriteCachedNeedsSync) +{ + void *alloc = malloc(8192 + 1024); + void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024); + + for (int i = 0; i < 4096; i++) + ((char *)buf)[i] = i; + + for (unsigned int heapMask : m_allHeaps) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + int map_fd = -1; + unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC; + + ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd)); + ASSERT_GE(map_fd, 0); + + void *ptr; + ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0); + ASSERT_TRUE(ptr != NULL); + + dirtyCache(ptr, 4096); + + writeKernel(map_fd, buf, 4096); + + for (int i = 0; i < 4096; i++) + ASSERT_EQ((char)i, ((char *)ptr)[i]) << i; + + ASSERT_EQ(0, munmap(ptr, 4096)); + ASSERT_EQ(0, close(map_fd)); + } + + free(alloc); +} + +TEST_F(Device, DMAReadCachedNeedsSync) +{ + void *alloc = malloc(8192 + 1024); + void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024); + + for (unsigned int heapMask : m_allHeaps) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + int map_fd = -1; + unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC; + + ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd)); + ASSERT_GE(map_fd, 0); + + void *ptr; + ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0); + ASSERT_TRUE(ptr != NULL); + + for (int i = 0; i < 4096; i++) + ((char *)ptr)[i] = i; + + ion_sync_fd(m_ionFd, map_fd); + + readDMA(map_fd, buf, 4096); + + for (int i = 0; i < 4096; i++) + ASSERT_EQ((char)i, ((char *)buf)[i]); + + ASSERT_EQ(0, munmap(ptr, 4096)); + ASSERT_EQ(0, close(map_fd)); + } + + free(alloc); +} + +TEST_F(Device, DMAWriteCachedNeedsSync) +{ + void *alloc = malloc(8192 + 1024); + void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024); + + for (int i = 0; i < 4096; i++) + ((char *)buf)[i] = i; + + for (unsigned int heapMask : m_allHeaps) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + int map_fd = -1; + unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC; + + ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd)); + ASSERT_GE(map_fd, 0); + + void *ptr; + ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0); + ASSERT_TRUE(ptr != NULL); + + dirtyCache(ptr, 4096); + + writeDMA(map_fd, buf, 4096); + + ion_sync_fd(m_ionFd, map_fd); + + for (int i = 0; i < 4096; i++) + ASSERT_EQ((char)i, ((char *)ptr)[i]) << i; + + ASSERT_EQ(0, munmap(ptr, 4096)); + ASSERT_EQ(0, close(map_fd)); + } + + free(alloc); +} +TEST_F(Device, KernelRead) +{ + void *alloc = malloc(8192 + 1024); + void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024); + + for (unsigned int heapMask : m_allHeaps) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + int map_fd = -1; + unsigned int flags = 0; + + ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd)); + ASSERT_GE(map_fd, 0); + + void *ptr; + ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0); + ASSERT_TRUE(ptr != NULL); + + for (int i = 0; i < 4096; i++) + ((char *)ptr)[i] = i; + + ((char*)buf)[4096] = 0x12; + readKernel(map_fd, buf, 4096); + ASSERT_EQ(((char*)buf)[4096], 0x12); + + for (int i = 0; i < 4096; i++) + ASSERT_EQ((char)i, ((char *)buf)[i]); + + ASSERT_EQ(0, munmap(ptr, 4096)); + ASSERT_EQ(0, close(map_fd)); + } + + free(alloc); +} + +TEST_F(Device, KernelWrite) +{ + void *alloc = malloc(8192 + 1024); + void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024); + + for (int i = 0; i < 4096; i++) + ((char *)buf)[i] = i; + + for (unsigned int heapMask : m_allHeaps) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + int map_fd = -1; + unsigned int flags = 0; + + ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd)); + ASSERT_GE(map_fd, 0); + + void *ptr; + ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0); + ASSERT_TRUE(ptr != NULL); + + dirtyCache(ptr, 4096); + + writeKernel(map_fd, buf, 4096); + + for (int i = 0; i < 4096; i++) + ASSERT_EQ((char)i, ((char *)ptr)[i]) << i; + + ASSERT_EQ(0, munmap(ptr, 4096)); + ASSERT_EQ(0, close(map_fd)); + } + + free(alloc); +} + +TEST_F(Device, DMARead) +{ + void *alloc = malloc(8192 + 1024); + void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024); + + for (unsigned int heapMask : m_allHeaps) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + int map_fd = -1; + unsigned int flags = 0; + + ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd)); + ASSERT_GE(map_fd, 0); + + void *ptr; + ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0); + ASSERT_TRUE(ptr != NULL); + + for (int i = 0; i < 4096; i++) + ((char *)ptr)[i] = i; + + readDMA(map_fd, buf, 4096); + + for (int i = 0; i < 4096; i++) + ASSERT_EQ((char)i, ((char *)buf)[i]); + + ASSERT_EQ(0, munmap(ptr, 4096)); + ASSERT_EQ(0, close(map_fd)); + } + + free(alloc); +} + +TEST_F(Device, DMAWrite) +{ + void *alloc = malloc(8192 + 1024); + void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024); + + for (int i = 0; i < 4096; i++) + ((char *)buf)[i] = i; + + for (unsigned int heapMask : m_allHeaps) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + int map_fd = -1; + unsigned int flags = 0; + + ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd)); + ASSERT_GE(map_fd, 0); + + void *ptr; + ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0); + ASSERT_TRUE(ptr != NULL); + + dirtyCache(ptr, 4096); + + writeDMA(map_fd, buf, 4096); + + for (int i = 0; i < 4096; i++) + ASSERT_EQ((char)i, ((char *)ptr)[i]) << i; + + ASSERT_EQ(0, munmap(ptr, 4096)); + ASSERT_EQ(0, close(map_fd)); + } + + free(alloc); +} + +TEST_F(Device, IsCached) +{ + void *buf = malloc(4096); + + for (unsigned int heapMask : m_allHeaps) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + int map_fd = -1; + unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC; + + ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd)); + ASSERT_GE(map_fd, 0); + + void *ptr; + ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0); + ASSERT_TRUE(ptr != NULL); + + dirtyCache(ptr, 4096); + + readDMA(map_fd, buf, 4096); + + bool same = true; + for (int i = 4096-16; i >= 0; i -= 16) + if (((char *)buf)[i] != i) + same = false; + ASSERT_FALSE(same); + + ASSERT_EQ(0, munmap(ptr, 4096)); + ASSERT_EQ(0, close(map_fd)); + } +} diff --git a/libion/tests/exit_test.cpp b/libion/tests/exit_test.cpp new file mode 100644 index 0000000..cdd3e27 --- /dev/null +++ b/libion/tests/exit_test.cpp @@ -0,0 +1,227 @@ +/* + * 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. + */ + +#include <sys/mman.h> + +#include <gtest/gtest.h> + +#include <ion/ion.h> + +#include "ion_test_fixture.h" + +class Exit : public IonAllHeapsTest { +}; + +TEST_F(Exit, WithAlloc) +{ + static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024}; + for (unsigned int heapMask : m_allHeaps) { + for (size_t size : allocationSizes) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + SCOPED_TRACE(::testing::Message() << "size " << size); + EXPECT_EXIT({ + ion_user_handle_t handle = 0; + + ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, 0, &handle)); + ASSERT_TRUE(handle != 0); + exit(0); + }, ::testing::ExitedWithCode(0), ""); + } + } +} + +TEST_F(Exit, WithAllocFd) +{ + static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024}; + for (unsigned int heapMask : m_allHeaps) { + for (size_t size : allocationSizes) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + SCOPED_TRACE(::testing::Message() << "size " << size); + EXPECT_EXIT({ + int handle_fd = -1; + + ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &handle_fd)); + ASSERT_NE(-1, handle_fd); + exit(0); + }, ::testing::ExitedWithCode(0), ""); + } + } +} + +TEST_F(Exit, WithRepeatedAllocFd) +{ + static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024}; + for (unsigned int heapMask : m_allHeaps) { + for (size_t size : allocationSizes) { + for (unsigned int i = 0; i < 1024; i++) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + SCOPED_TRACE(::testing::Message() << "size " << size); + ASSERT_EXIT({ + int handle_fd = -1; + + ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &handle_fd)); + ASSERT_NE(-1, handle_fd); + exit(0); + }, ::testing::ExitedWithCode(0), "") + << "failed on heap " << heapMask + << " and size " << size + << " on iteration " << i; + } + } + } +} + + +TEST_F(Exit, WithMapping) +{ + static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024}; + for (unsigned int heapMask : m_allHeaps) { + for (size_t size : allocationSizes) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + SCOPED_TRACE(::testing::Message() << "size " << size); + EXPECT_EXIT({ + int map_fd = -1; + + ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &map_fd)); + ASSERT_GE(map_fd, 0); + + void *ptr; + ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0); + ASSERT_TRUE(ptr != NULL); + exit(0); + }, ::testing::ExitedWithCode(0), ""); + } + } + +} + +TEST_F(Exit, WithPartialMapping) +{ + static const size_t allocationSizes[] = {64*1024, 1024*1024, 2*1024*1024}; + for (unsigned int heapMask : m_allHeaps) { + for (size_t size : allocationSizes) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + SCOPED_TRACE(::testing::Message() << "size " << size); + EXPECT_EXIT({ + int map_fd = -1; + + ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &map_fd)); + ASSERT_GE(map_fd, 0); + + void *ptr; + ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0); + ASSERT_TRUE(ptr != NULL); + + ASSERT_EQ(0, munmap(ptr, size / 2)); + exit(0); + }, ::testing::ExitedWithCode(0), ""); + } + } +} + +TEST_F(Exit, WithMappingCached) +{ + static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024}; + for (unsigned int heapMask : m_allHeaps) { + for (size_t size : allocationSizes) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + SCOPED_TRACE(::testing::Message() << "size " << size); + EXPECT_EXIT({ + int map_fd = -1; + + ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED, &map_fd)); + ASSERT_GE(map_fd, 0); + + void *ptr; + ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0); + ASSERT_TRUE(ptr != NULL); + exit(0); + }, ::testing::ExitedWithCode(0), ""); + } + } + +} + +TEST_F(Exit, WithPartialMappingCached) +{ + static const size_t allocationSizes[] = {64*1024, 1024*1024, 2*1024*1024}; + for (unsigned int heapMask : m_allHeaps) { + for (size_t size : allocationSizes) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + SCOPED_TRACE(::testing::Message() << "size " << size); + EXPECT_EXIT({ + int map_fd = -1; + + ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED, &map_fd)); + ASSERT_GE(map_fd, 0); + + void *ptr; + ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0); + ASSERT_TRUE(ptr != NULL); + + ASSERT_EQ(0, munmap(ptr, size / 2)); + exit(0); + }, ::testing::ExitedWithCode(0), ""); + } + } +} + +TEST_F(Exit, WithMappingNeedsSync) +{ + static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024}; + for (unsigned int heapMask : m_allHeaps) { + for (size_t size : allocationSizes) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + SCOPED_TRACE(::testing::Message() << "size " << size); + EXPECT_EXIT({ + int map_fd = -1; + + ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC, &map_fd)); + ASSERT_GE(map_fd, 0); + + void *ptr; + ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0); + ASSERT_TRUE(ptr != NULL); + exit(0); + }, ::testing::ExitedWithCode(0), ""); + } + } + +} + +TEST_F(Exit, WithPartialMappingNeedsSync) +{ + static const size_t allocationSizes[] = {64*1024, 1024*1024, 2*1024*1024}; + for (unsigned int heapMask : m_allHeaps) { + for (size_t size : allocationSizes) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + SCOPED_TRACE(::testing::Message() << "size " << size); + EXPECT_EXIT({ + int map_fd = -1; + + ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC, &map_fd)); + ASSERT_GE(map_fd, 0); + + void *ptr; + ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0); + ASSERT_TRUE(ptr != NULL); + + ASSERT_EQ(0, munmap(ptr, size / 2)); + exit(0); + }, ::testing::ExitedWithCode(0), ""); + } + } +} diff --git a/libion/tests/formerly_valid_handle_test.cpp b/libion/tests/formerly_valid_handle_test.cpp new file mode 100644 index 0000000..01ab8f3 --- /dev/null +++ b/libion/tests/formerly_valid_handle_test.cpp @@ -0,0 +1,63 @@ +/* + * 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. + */ + +#include <sys/mman.h> + +#include <gtest/gtest.h> + +#include <ion/ion.h> + +#include "ion_test_fixture.h" + +class FormerlyValidHandle : public IonTest { + public: + virtual void SetUp(); + virtual void TearDown(); + ion_user_handle_t m_handle; +}; + +void FormerlyValidHandle::SetUp() +{ + IonTest::SetUp(); + ASSERT_EQ(0, ion_alloc(m_ionFd, 4096, 0, 1/* ion_env->m_firstHeap */, 0, &m_handle)); + ASSERT_TRUE(m_handle != 0); + ASSERT_EQ(0, ion_free(m_ionFd, m_handle)); +} + +void FormerlyValidHandle::TearDown() +{ + m_handle = 0; +} + +TEST_F(FormerlyValidHandle, free) +{ + ASSERT_EQ(-EINVAL, ion_free(m_ionFd, m_handle)); +} + +TEST_F(FormerlyValidHandle, map) +{ + int map_fd; + unsigned char *ptr; + + ASSERT_EQ(-EINVAL, ion_map(m_ionFd, m_handle, 4096, PROT_READ, 0, 0, &ptr, &map_fd)); +} + +TEST_F(FormerlyValidHandle, share) +{ + int share_fd; + + ASSERT_EQ(-EINVAL, ion_share(m_ionFd, m_handle, &share_fd)); +} diff --git a/libion/tests/invalid_values_test.cpp b/libion/tests/invalid_values_test.cpp new file mode 100644 index 0000000..77fea17 --- /dev/null +++ b/libion/tests/invalid_values_test.cpp @@ -0,0 +1,186 @@ +/* + * 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. + */ + +#include <sys/mman.h> + +#include <gtest/gtest.h> + +#include <ion/ion.h> + +#include "ion_test_fixture.h" + +class InvalidValues : public IonAllHeapsTest { + public: + virtual void SetUp(); + virtual void TearDown(); + ion_user_handle_t m_validHandle; + int m_validShareFd; + ion_user_handle_t const m_badHandle = -1; +}; + +void InvalidValues::SetUp() +{ + IonAllHeapsTest::SetUp(); + ASSERT_EQ(0, ion_alloc(m_ionFd, 4096, 0, m_firstHeap, 0, &m_validHandle)) + << m_ionFd << " " << m_firstHeap; + ASSERT_TRUE(m_validHandle != 0); + ASSERT_EQ(0, ion_share(m_ionFd, m_validHandle, &m_validShareFd)); +} + +void InvalidValues::TearDown() +{ + ASSERT_EQ(0, ion_free(m_ionFd, m_validHandle)); + ASSERT_EQ(0, close(m_validShareFd)); + m_validHandle = 0; + IonAllHeapsTest::TearDown(); +} + +TEST_F(InvalidValues, ion_close) +{ + EXPECT_EQ(-EBADF, ion_close(-1)); +} + +TEST_F(InvalidValues, ion_alloc) +{ + ion_user_handle_t handle; + /* invalid ion_fd */ + int ret = ion_alloc(0, 4096, 0, m_firstHeap, 0, &handle); + EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY); + /* invalid ion_fd */ + EXPECT_EQ(-EBADF, ion_alloc(-1, 4096, 0, m_firstHeap, 0, &handle)); + /* no heaps */ + EXPECT_EQ(-ENODEV, ion_alloc(m_ionFd, 4096, 0, 0, 0, &handle)); + for (unsigned int heapMask : m_allHeaps) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + /* zero size */ + EXPECT_EQ(-EINVAL, ion_alloc(m_ionFd, 0, 0, heapMask, 0, &handle)); + /* too large size */ + EXPECT_EQ(-EINVAL, ion_alloc(m_ionFd, -1, 0, heapMask, 0, &handle)); + /* bad alignment */ + EXPECT_EQ(-EINVAL, ion_alloc(m_ionFd, 4096, -1, heapMask, 0, &handle)); + /* NULL handle */ + EXPECT_EQ(-EINVAL, ion_alloc(m_ionFd, 4096, 0, heapMask, 0, NULL)); + } +} + +TEST_F(InvalidValues, ion_alloc_fd) +{ + int fd; + /* invalid ion_fd */ + int ret = ion_alloc_fd(0, 4096, 0, m_firstHeap, 0, &fd); + EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY); + /* invalid ion_fd */ + EXPECT_EQ(-EBADF, ion_alloc_fd(-1, 4096, 0, m_firstHeap, 0, &fd)); + /* no heaps */ + EXPECT_EQ(-ENODEV, ion_alloc_fd(m_ionFd, 4096, 0, 0, 0, &fd)); + for (unsigned int heapMask : m_allHeaps) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + /* zero size */ + EXPECT_EQ(-EINVAL, ion_alloc_fd(m_ionFd, 0, 0, heapMask, 0, &fd)); + /* too large size */ + EXPECT_EQ(-EINVAL, ion_alloc_fd(m_ionFd, -1, 0, heapMask, 0, &fd)); + /* bad alignment */ + EXPECT_EQ(-EINVAL, ion_alloc_fd(m_ionFd, 4096, -1, heapMask, 0, &fd)); + /* NULL handle */ + EXPECT_EQ(-EINVAL, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, 0, NULL)); + } +} + +TEST_F(InvalidValues, ion_free) +{ + /* invalid ion fd */ + int ret = ion_free(0, m_validHandle); + EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY); + /* invalid ion fd */ + EXPECT_EQ(-EBADF, ion_free(-1, m_validHandle)); + /* zero handle */ + EXPECT_EQ(-EINVAL, ion_free(m_ionFd, 0)); + /* bad handle */ + EXPECT_EQ(-EINVAL, ion_free(m_ionFd, m_badHandle)); +} + +TEST_F(InvalidValues, ion_map) +{ + int map_fd; + unsigned char *ptr; + + /* invalid ion fd */ + int ret = ion_map(0, m_validHandle, 4096, PROT_READ, 0, 0, &ptr, &map_fd); + EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY); + /* invalid ion fd */ + EXPECT_EQ(-EBADF, ion_map(-1, m_validHandle, 4096, PROT_READ, 0, 0, &ptr, &map_fd)); + /* zero handle */ + EXPECT_EQ(-EINVAL, ion_map(m_ionFd, 0, 4096, PROT_READ, 0, 0, &ptr, &map_fd)); + /* bad handle */ + EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_badHandle, 4096, PROT_READ, 0, 0, &ptr, &map_fd)); + /* zero length */ + EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 0, PROT_READ, 0, 0, &ptr, &map_fd)); + /* bad prot */ + EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 4096, -1, 0, 0, &ptr, &map_fd)); + /* bad offset */ + EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 4096, PROT_READ, 0, -1, &ptr, &map_fd)); + /* NULL ptr */ + EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 4096, PROT_READ, 0, 0, NULL, &map_fd)); + /* NULL map_fd */ + EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 4096, PROT_READ, 0, 0, &ptr, NULL)); +} + +TEST_F(InvalidValues, ion_share) +{ + int share_fd; + + /* invalid ion fd */ + int ret = ion_share(0, m_validHandle, &share_fd); + EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY); + /* invalid ion fd */ + EXPECT_EQ(-EBADF, ion_share(-1, m_validHandle, &share_fd)); + /* zero handle */ + EXPECT_EQ(-EINVAL, ion_share(m_ionFd, 0, &share_fd)); + /* bad handle */ + EXPECT_EQ(-EINVAL, ion_share(m_ionFd, m_badHandle, &share_fd)); + /* NULL share_fd */ + EXPECT_EQ(-EINVAL, ion_share(m_ionFd, m_validHandle, NULL)); +} + +TEST_F(InvalidValues, ion_import) +{ + ion_user_handle_t handle; + + /* invalid ion fd */ + int ret = ion_import(0, m_validShareFd, &handle); + EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY); + /* invalid ion fd */ + EXPECT_EQ(-EBADF, ion_import(-1, m_validShareFd, &handle)); + /* bad share_fd */ + EXPECT_EQ(-EINVAL, ion_import(m_ionFd, 0, &handle)); + /* invalid share_fd */ + EXPECT_EQ(-EBADF, ion_import(m_ionFd, -1, &handle)); + /* NULL handle */ + EXPECT_EQ(-EINVAL, ion_import(m_ionFd, m_validShareFd, NULL)); +} + +TEST_F(InvalidValues, ion_sync_fd) +{ + /* invalid ion fd */ + int ret = ion_sync_fd(0, m_validShareFd); + EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY); + /* invalid ion fd */ + EXPECT_EQ(-EBADF, ion_sync_fd(-1, m_validShareFd)); + /* bad share_fd */ + EXPECT_EQ(-EINVAL, ion_sync_fd(m_ionFd, 0)); + /* invalid share_fd */ + EXPECT_EQ(-EBADF, ion_sync_fd(m_ionFd, -1)); +} diff --git a/libion/tests/ion_test_fixture.cpp b/libion/tests/ion_test_fixture.cpp new file mode 100644 index 0000000..e20c730 --- /dev/null +++ b/libion/tests/ion_test_fixture.cpp @@ -0,0 +1,73 @@ +/* + * 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. + */ + +#include <gtest/gtest.h> + +#include <ion/ion.h> + +#include "ion_test_fixture.h" + +IonTest::IonTest() : m_ionFd(-1) +{ +} + +void IonTest::SetUp() { + m_ionFd = ion_open(); + ASSERT_GE(m_ionFd, 0); +} + +void IonTest::TearDown() { + ion_close(m_ionFd); +} + +IonAllHeapsTest::IonAllHeapsTest() : + m_firstHeap(0), + m_lastHeap(0), + m_allHeaps() +{ +} + +void IonAllHeapsTest::SetUp() { + int fd = ion_open(); + ASSERT_GE(fd, 0); + + for (int i = 1; i != 0; i <<= 1) { + ion_user_handle_t handle = 0; + int ret; + ret = ion_alloc(fd, 4096, 0, i, 0, &handle); + if (ret == 0 && handle != 0) { + ion_free(fd, handle); + if (!m_firstHeap) { + m_firstHeap = i; + } + m_lastHeap = i; + m_allHeaps.push_back(i); + } else { + ASSERT_EQ(-ENODEV, ret); + } + } + ion_close(fd); + + EXPECT_NE(0U, m_firstHeap); + EXPECT_NE(0U, m_lastHeap); + + RecordProperty("Heaps", m_allHeaps.size()); + IonTest::SetUp(); +} + +void IonAllHeapsTest::TearDown() { + IonTest::TearDown(); +} diff --git a/libion/tests/ion_test_fixture.h b/libion/tests/ion_test_fixture.h new file mode 100644 index 0000000..4098214 --- /dev/null +++ b/libion/tests/ion_test_fixture.h @@ -0,0 +1,46 @@ +/* + * 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. + */ + +#ifndef ION_TEST_FIXTURE_H_ +#define ION_TEST_FIXTURE_H_ + +#include <gtest/gtest.h> + +using ::testing::Test; + +class IonTest : public virtual Test { + public: + IonTest(); + virtual ~IonTest() {}; + virtual void SetUp(); + virtual void TearDown(); + int m_ionFd; +}; + +class IonAllHeapsTest : public IonTest { + public: + IonAllHeapsTest(); + virtual ~IonAllHeapsTest() {}; + virtual void SetUp(); + virtual void TearDown(); + + unsigned int m_firstHeap; + unsigned int m_lastHeap; + + std::vector<unsigned int> m_allHeaps; +}; + +#endif /* ION_TEST_FIXTURE_H_ */ diff --git a/libion/tests/map_test.cpp b/libion/tests/map_test.cpp new file mode 100644 index 0000000..c006dc8 --- /dev/null +++ b/libion/tests/map_test.cpp @@ -0,0 +1,162 @@ +/* + * 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. + */ + +#include <sys/mman.h> + +#include <gtest/gtest.h> + +#include <ion/ion.h> + +#include "ion_test_fixture.h" + +class Map : public IonAllHeapsTest { +}; + +TEST_F(Map, MapHandle) +{ + static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024}; + for (unsigned int heapMask : m_allHeaps) { + for (size_t size : allocationSizes) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + SCOPED_TRACE(::testing::Message() << "size " << size); + ion_user_handle_t handle = 0; + + ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, 0, &handle)); + ASSERT_TRUE(handle != 0); + + int map_fd = -1; + unsigned char *ptr = NULL; + ASSERT_EQ(0, ion_map(m_ionFd, handle, size, PROT_READ | PROT_WRITE, MAP_SHARED, 0, &ptr, &map_fd)); + ASSERT_TRUE(ptr != NULL); + ASSERT_GE(map_fd, 0); + + ASSERT_EQ(0, close(map_fd)); + + ASSERT_EQ(0, ion_free(m_ionFd, handle)); + + memset(ptr, 0xaa, size); + + ASSERT_EQ(0, munmap(ptr, size)); + } + } +} + +TEST_F(Map, MapFd) +{ + static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024}; + for (unsigned int heapMask : m_allHeaps) { + for (size_t size : allocationSizes) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + SCOPED_TRACE(::testing::Message() << "size " << size); + int map_fd = -1; + + ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &map_fd)); + ASSERT_GE(map_fd, 0); + + void *ptr; + ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0); + ASSERT_TRUE(ptr != NULL); + + ASSERT_EQ(0, close(map_fd)); + + memset(ptr, 0xaa, size); + + ASSERT_EQ(0, munmap(ptr, size)); + } + } +} + +TEST_F(Map, MapOffset) +{ + for (unsigned int heapMask : m_allHeaps) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + int map_fd = -1; + + ASSERT_EQ(0, ion_alloc_fd(m_ionFd, PAGE_SIZE * 2, 0, heapMask, 0, &map_fd)); + ASSERT_GE(map_fd, 0); + + unsigned char *ptr; + ptr = (unsigned char *)mmap(NULL, PAGE_SIZE * 2, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0); + ASSERT_TRUE(ptr != NULL); + + memset(ptr, 0, PAGE_SIZE); + memset(ptr + PAGE_SIZE, 0xaa, PAGE_SIZE); + + ASSERT_EQ(0, munmap(ptr, PAGE_SIZE * 2)); + + ptr = (unsigned char *)mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, PAGE_SIZE); + ASSERT_TRUE(ptr != NULL); + + ASSERT_EQ(ptr[0], 0xaa); + ASSERT_EQ(ptr[PAGE_SIZE - 1], 0xaa); + + ASSERT_EQ(0, munmap(ptr, PAGE_SIZE)); + + ASSERT_EQ(0, close(map_fd)); + } +} + +TEST_F(Map, MapCached) +{ + static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024}; + for (unsigned int heapMask : m_allHeaps) { + for (size_t size : allocationSizes) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + SCOPED_TRACE(::testing::Message() << "size " << size); + int map_fd = -1; + unsigned int flags = ION_FLAG_CACHED; + + ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, flags, &map_fd)); + ASSERT_GE(map_fd, 0); + + void *ptr; + ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0); + ASSERT_TRUE(ptr != NULL); + + ASSERT_EQ(0, close(map_fd)); + + memset(ptr, 0xaa, size); + + ASSERT_EQ(0, munmap(ptr, size)); + } + } +} + +TEST_F(Map, MapCachedNeedsSync) +{ + static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024}; + for (unsigned int heapMask : m_allHeaps) { + for (size_t size : allocationSizes) { + SCOPED_TRACE(::testing::Message() << "heap " << heapMask); + SCOPED_TRACE(::testing::Message() << "size " << size); + int map_fd = -1; + unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC; + + ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, flags, &map_fd)); + ASSERT_GE(map_fd, 0); + + void *ptr; + ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0); + ASSERT_TRUE(ptr != NULL); + + ASSERT_EQ(0, close(map_fd)); + + memset(ptr, 0xaa, size); + + ASSERT_EQ(0, munmap(ptr, size)); + } + } +} diff --git a/liblinenoise/Android.mk b/liblinenoise/Android.mk deleted file mode 100644 index b32a5f1..0000000 --- a/liblinenoise/Android.mk +++ /dev/null @@ -1,12 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -# Static library -# ======================================================== - -include $(CLEAR_VARS) -LOCAL_MODULE:= liblinenoise -LOCAL_SRC_FILES := linenoise.c - - -include $(BUILD_STATIC_LIBRARY) diff --git a/liblinenoise/MODULE_LICENSE_BSD_LIKE b/liblinenoise/MODULE_LICENSE_BSD_LIKE deleted file mode 100644 index e69de29..0000000 --- a/liblinenoise/MODULE_LICENSE_BSD_LIKE +++ /dev/null diff --git a/liblinenoise/NOTICE b/liblinenoise/NOTICE deleted file mode 100644 index f61419e..0000000 --- a/liblinenoise/NOTICE +++ /dev/null @@ -1,28 +0,0 @@ -Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot 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: - - * 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 Redis nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file diff --git a/liblinenoise/linenoise.c b/liblinenoise/linenoise.c deleted file mode 100644 index 4f6775c..0000000 --- a/liblinenoise/linenoise.c +++ /dev/null @@ -1,449 +0,0 @@ -/* linenoise.c -- guerrilla line editing library against the idea that a - * line editing lib needs to be 20,000 lines of C code. - * - * You can find the latest source code at: - * - * http://github.com/antirez/linenoise - * - * Does a number of crazy assumptions that happen to be true in 99.9999% of - * the 2010 UNIX computers around. - * - * Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot 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: - * - * * 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 Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * References: - * - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html - * - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html - * - * Todo list: - * - Switch to gets() if $TERM is something we can't support. - * - Filter bogus Ctrl+<char> combinations. - * - Win32 support - * - * Bloat: - * - Completion? - * - History search like Ctrl+r in readline? - * - * List of escape sequences used by this program, we do everything just - * with three sequences. In order to be so cheap we may have some - * flickering effect with some slow terminal, but the lesser sequences - * the more compatible. - * - * CHA (Cursor Horizontal Absolute) - * Sequence: ESC [ n G - * Effect: moves cursor to column n - * - * EL (Erase Line) - * Sequence: ESC [ n K - * Effect: if n is 0 or missing, clear from cursor to end of line - * Effect: if n is 1, clear from beginning of line to cursor - * Effect: if n is 2, clear entire line - * - * CUF (CUrsor Forward) - * Sequence: ESC [ n C - * Effect: moves cursor forward of n chars - * - */ - -#include <termios.h> -#include <unistd.h> -#include <stdlib.h> -#include <stdio.h> -#include <errno.h> -#include <string.h> -#include <stdlib.h> -#include <sys/types.h> -#include <sys/ioctl.h> -#include <unistd.h> - -#define LINENOISE_MAX_LINE 4096 -static char *unsupported_term[] = {"dumb","cons25",NULL}; - -static struct termios orig_termios; /* in order to restore at exit */ -static int rawmode = 0; /* for atexit() function to check if restore is needed*/ -static int atexit_registered = 0; /* register atexit just 1 time */ -static int history_max_len = 100; -static int history_len = 0; -char **history = NULL; - -static void linenoiseAtExit(void); -int linenoiseHistoryAdd(const char *line); - -static int isUnsupportedTerm(void) { - char *term = getenv("TERM"); - int j; - - if (term == NULL) return 0; - for (j = 0; unsupported_term[j]; j++) - if (!strcasecmp(term,unsupported_term[j])) return 1; - return 0; -} - -static void freeHistory(void) { - if (history) { - int j; - - for (j = 0; j < history_len; j++) - free(history[j]); - free(history); - } -} - -static int enableRawMode(int fd) { - struct termios raw; - - if (!isatty(STDIN_FILENO)) goto fatal; - if (!atexit_registered) { - atexit(linenoiseAtExit); - atexit_registered = 1; - } - if (tcgetattr(fd,&orig_termios) == -1) goto fatal; - - raw = orig_termios; /* modify the original mode */ - /* input modes: no break, no CR to NL, no parity check, no strip char, - * no start/stop output control. */ - raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); - /* output modes - disable post processing */ - raw.c_oflag &= ~(OPOST); - /* control modes - set 8 bit chars */ - raw.c_cflag |= (CS8); - /* local modes - choing off, canonical off, no extended functions, - * no signal chars (^Z,^C) */ - raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); - /* control chars - set return condition: min number of bytes and timer. - * We want read to return every single byte, without timeout. */ - raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */ - - /* put terminal in raw mode */ - if (tcsetattr(fd,TCSADRAIN,&raw) < 0) goto fatal; - rawmode = 1; - return 0; - -fatal: - errno = ENOTTY; - return -1; -} - -static void disableRawMode(int fd) { - /* Don't even check the return value as it's too late. */ - if (rawmode && tcsetattr(fd,TCSADRAIN,&orig_termios) != -1) - rawmode = 0; -} - -/* At exit we'll try to fix the terminal to the initial conditions. */ -static void linenoiseAtExit(void) { - disableRawMode(STDIN_FILENO); - freeHistory(); -} - -static int getColumns(void) { - struct winsize ws; - - if (ioctl(1, TIOCGWINSZ, &ws) == -1) return 4096; - if (ws.ws_col == 0) { - return 4096; - } - return ws.ws_col; -} - -static int effectiveLen(const char* prompt) { - int col = 0; - char c; - // TODO: Handle escape sequences. - while ( (c = *prompt++) != 0 ) { - if (c == '\n') { - col = 0; - } else { - col++; - } - } - return col; -} - -static void refreshLine(int fd, const char *prompt, char *buf, size_t len, size_t pos, size_t cols) { - char seq[64]; - size_t plen = effectiveLen(prompt); - - while((plen+pos) >= cols) { - buf++; - len--; - pos--; - } - while (plen+len > cols) { - len--; - } - - /* Cursor to left edge */ - snprintf(seq,64,"\x1b[0G"); - if (write(fd,seq,strlen(seq)) == -1) return; - /* Write the prompt and the current buffer content */ - if (write(fd,prompt,strlen(prompt)) == -1) return; - if (write(fd,buf,len) == -1) return; - /* Erase to right */ - snprintf(seq,64,"\x1b[0K"); - if (write(fd,seq,strlen(seq)) == -1) return; - /* Move cursor to original position. */ - snprintf(seq,64,"\x1b[0G\x1b[%dC", (int)(pos+plen)); - if (write(fd,seq,strlen(seq)) == -1) return; -} - -static int linenoisePrompt(int fd, char *buf, size_t buflen, const char *prompt) { - size_t plen = strlen(prompt); - size_t pos = 0; - size_t len = 0; - size_t cols = getColumns(); - int history_index = 0; - - buf[0] = '\0'; - buflen--; /* Make sure there is always space for the nulterm */ - - /* The latest history entry is always our current buffer, that - * initially is just an empty string. */ - linenoiseHistoryAdd(""); - - if (write(fd,prompt,plen) == -1) return -1; - while(1) { - char c; - int nread; - char seq[2]; - - nread = read(fd,&c,1); - if (nread <= 0) return len; - switch(c) { - case 10: /* line feed. */ - case 13: /* enter */ - history_len--; - return len; - case 4: /* ctrl-d */ - history_len--; - return (len == 0) ? -1 : (int)len; - case 3: /* ctrl-c */ - errno = EAGAIN; - return -1; - case 127: /* backspace */ - case 8: /* ctrl-h */ - if (pos > 0 && len > 0) { - memmove(buf+pos-1,buf+pos,len-pos); - pos--; - len--; - buf[len] = '\0'; - refreshLine(fd,prompt,buf,len,pos,cols); - } - break; - case 20: /* ctrl-t */ - if (pos > 0 && pos < len) { - int aux = buf[pos-1]; - buf[pos-1] = buf[pos]; - buf[pos] = aux; - if (pos != len-1) pos++; - refreshLine(fd,prompt,buf,len,pos,cols); - } - break; - case 2: /* ctrl-b */ - goto left_arrow; - case 6: /* ctrl-f */ - goto right_arrow; - case 16: /* ctrl-p */ - seq[1] = 65; - goto up_down_arrow; - case 14: /* ctrl-n */ - seq[1] = 66; - goto up_down_arrow; - break; - case 27: /* escape sequence */ - if (read(fd,seq,2) == -1) break; - if (seq[0] == 91 && seq[1] == 68) { -left_arrow: - /* left arrow */ - if (pos > 0) { - pos--; - refreshLine(fd,prompt,buf,len,pos,cols); - } - } else if (seq[0] == 91 && seq[1] == 67) { -right_arrow: - /* right arrow */ - if (pos != len) { - pos++; - refreshLine(fd,prompt,buf,len,pos,cols); - } - } else if (seq[0] == 91 && (seq[1] == 65 || seq[1] == 66)) { -up_down_arrow: - /* up and down arrow: history */ - if (history_len > 1) { - /* Update the current history entry before to - * overwrite it with tne next one. */ - free(history[history_len-1-history_index]); - history[history_len-1-history_index] = strdup(buf); - /* Show the new entry */ - history_index += (seq[1] == 65) ? 1 : -1; - if (history_index < 0) { - history_index = 0; - break; - } else if (history_index >= history_len) { - history_index = history_len-1; - break; - } - strncpy(buf,history[history_len-1-history_index],buflen); - buf[buflen] = '\0'; - len = pos = strlen(buf); - refreshLine(fd,prompt,buf,len,pos,cols); - } - } - break; - default: - if (len < buflen) { - if (len == pos) { - buf[pos] = c; - pos++; - len++; - buf[len] = '\0'; - if (plen+len < cols) { - /* Avoid a full update of the line in the - * trivial case. */ - if (write(fd,&c,1) == -1) return -1; - } else { - refreshLine(fd,prompt,buf,len,pos,cols); - } - } else { - memmove(buf+pos+1,buf+pos,len-pos); - buf[pos] = c; - len++; - pos++; - buf[len] = '\0'; - refreshLine(fd,prompt,buf,len,pos,cols); - } - } - break; - case 21: /* Ctrl+u, delete the whole line. */ - buf[0] = '\0'; - pos = len = 0; - refreshLine(fd,prompt,buf,len,pos,cols); - break; - case 11: /* Ctrl+k, delete from current to end of line. */ - buf[pos] = '\0'; - len = pos; - refreshLine(fd,prompt,buf,len,pos,cols); - break; - case 1: /* Ctrl+a, go to the start of the line */ - pos = 0; - refreshLine(fd,prompt,buf,len,pos,cols); - break; - case 5: /* ctrl+e, go to the end of the line */ - pos = len; - refreshLine(fd,prompt,buf,len,pos,cols); - break; - } - } - return len; -} - -static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) { - int fd = STDIN_FILENO; - int count; - - if (buflen == 0) { - errno = EINVAL; - return -1; - } - if (!isatty(STDIN_FILENO)) { - if (fgets(buf, buflen, stdin) == NULL) return -1; - count = strlen(buf); - if (count && buf[count-1] == '\n') { - count--; - buf[count] = '\0'; - } - } else { - if (enableRawMode(fd) == -1) return -1; - count = linenoisePrompt(fd, buf, buflen, prompt); - disableRawMode(fd); - } - return count; -} - -char *linenoise(const char *prompt) { - char buf[LINENOISE_MAX_LINE]; - int count; - - if (isUnsupportedTerm()) { - size_t len; - - printf("%s",prompt); - fflush(stdout); - if (fgets(buf,LINENOISE_MAX_LINE,stdin) == NULL) return NULL; - len = strlen(buf); - while(len && (buf[len-1] == '\n' || buf[len-1] == '\r')) { - len--; - buf[len] = '\0'; - } - return strdup(buf); - } else { - count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt); - if (count == -1) return NULL; - return strdup(buf); - } -} - -/* Using a circular buffer is smarter, but a bit more complex to handle. */ -int linenoiseHistoryAdd(const char *line) { - char *linecopy; - - if (history_max_len == 0) return 0; - if (history == 0) { - history = malloc(sizeof(char*)*history_max_len); - if (history == NULL) return 0; - memset(history,0,(sizeof(char*)*history_max_len)); - } - linecopy = strdup(line); - if (!linecopy) return 0; - if (history_len == history_max_len) { - memmove(history,history+1,sizeof(char*)*(history_max_len-1)); - history_len--; - } - history[history_len] = linecopy; - history_len++; - return 1; -} - -int linenoiseHistorySetMaxLen(int len) { - char **new; - - if (len < 1) return 0; - if (history) { - int tocopy = history_len; - - new = malloc(sizeof(char*)*len); - if (new == NULL) return 0; - if (len < tocopy) tocopy = len; - memcpy(new,history+(history_max_len-tocopy), sizeof(char*)*tocopy); - free(history); - history = new; - } - history_max_len = len; - if (history_len > history_max_len) - history_len = history_max_len; - return 1; -} diff --git a/liblinenoise/linenoise.h b/liblinenoise/linenoise.h deleted file mode 100644 index 57bf9d1..0000000 --- a/liblinenoise/linenoise.h +++ /dev/null @@ -1,41 +0,0 @@ -/* linenoise.h -- guerrilla line editing library against the idea that a - * line editing lib needs to be 20,000 lines of C code. - * - * See linenoise.c for more information. - * - * Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot 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: - * - * * 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 Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __LINENOISE_H -#define __LINENOISE_H - -char *linenoise(const char *prompt); -int linenoiseHistoryAdd(const char *line); -int linenoiseHistorySetMaxLen(int len); - -#endif /* __LINENOISE_H */ diff --git a/liblog/Android.mk b/liblog/Android.mk index 6bfb119..0d6c970 100644 --- a/liblog/Android.mk +++ b/liblog/Android.mk @@ -1,5 +1,5 @@ # -# Copyright (C) 2008 The Android Open Source Project +# Copyright (C) 2008-2014 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. @@ -42,7 +42,7 @@ else endif liblog_host_sources := $(liblog_sources) fake_log_device.c - +liblog_target_sources = $(liblog_sources) log_read.c # Shared and static library for host # ======================================================== @@ -67,15 +67,16 @@ LOCAL_LDLIBS := -lpthread LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 -m64 include $(BUILD_HOST_STATIC_LIBRARY) - # Shared and static library for target # ======================================================== include $(CLEAR_VARS) LOCAL_MODULE := liblog -LOCAL_SRC_FILES := $(liblog_sources) +LOCAL_SRC_FILES := $(liblog_target_sources) include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := liblog LOCAL_WHOLE_STATIC_LIBRARIES := liblog include $(BUILD_SHARED_LIBRARY) + +include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/liblog/NOTICE b/liblog/NOTICE index c5b1efa..06a9081 100644 --- a/liblog/NOTICE +++ b/liblog/NOTICE @@ -1,5 +1,5 @@ - Copyright (c) 2005-2008, The Android Open Source Project + Copyright (c) 2005-2014, 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. diff --git a/liblog/README b/liblog/README new file mode 100644 index 0000000..d7472e4 --- /dev/null +++ b/liblog/README @@ -0,0 +1,134 @@ +LIBLOG(3) Android NDK Programming Manual LIBLOG(3) + + + +NAME + liblog - Android NDK logger interfaces + +SYNOPSIS + #include <log/log.h> + + ALOG(android_priority, tag, format, ...) + IF_ALOG(android_priority, tag) + LOG_PRI(priority, tag, format, ...) + LOG_PRI_VA(priority, tag, format, args) + #define LOG_TAG NULL + ALOGV(format, ...) + SLOGV(format, ...) + RLOGV(format, ...) + ALOGV_IF(cond, format, ...) + SLOGV_IF(cond, format, ...) + RLOGV_IF(cond, format, ...) + IF_ALOGC() + ALOGD(format, ...) + SLOGD(format, ...) + RLOGD(format, ...) + ALOGD_IF(cond, format, ...) + SLOGD_IF(cond, format, ...) + RLOGD_IF(cond, format, ...) + IF_ALOGD() + ALOGI(format, ...) + SLOGI(format, ...) + RLOGI(format, ...) + ALOGI_IF(cond, format, ...) + SLOGI_IF(cond, format, ...) + RLOGI_IF(cond, format, ...) + IF_ALOGI() + ALOGW(format, ...) + SLOGW(format, ...) + RLOGW(format, ...) + ALOGW_IF(cond, format, ...) + SLOGW_IF(cond, format, ...) + RLOGW_IF(cond, format, ...) + IF_ALOGW() + ALOGE(format, ...) + SLOGE(format, ...) + RLOGE(format, ...) + ALOGE_IF(cond, format, ...) + SLOGE_IF(cond, format, ...) + RLOGE_IF(cond, format, ...) + IF_ALOGE() + LOG_FATAL(format, ...) + LOG_ALWAYS_FATAL(format, ...) + LOG_FATAL_IF(cond, format, ...) + LOG_ALWAYS_FATAL_IF(cond, format, ...) + ALOG_ASSERT(cond, format, ...) + LOG_EVENT_INT(tag, value) + LOG_EVENT_LONG(tag, value) + + Link with -llog + + #include <log/logger.h> + + log_id_t android_logger_get_id(struct logger *logger) + int android_logger_clear(struct logger *logger) + int android_logger_get_log_size(struct logger *logger) + int android_logger_get_log_readable_size(struct logger *logger) + int android_logger_get_log_version(struct logger *logger) + + struct logger_list *android_logger_list_alloc(int mode, unsigned int + tail, pid_t pid) + struct logger *android_logger_open(struct logger_list *logger_list, + log_id_t id) + struct logger_list *android_logger_list_open(log_id_t id, int mode, + unsigned int tail, pid_t pid) + + int android_logger_list_read(struct logger_list *logger_list, struct + log_msg *log_msg + + void android_logger_list_free(struct logger_list *logger_list) + + log_id_t android_name_to_log_id(const char *logName) + const char *android_log_id_to_name(log_id_t log_id) + + Link with -llog + +DESCRIPTION + liblog represents an interface to the volatile Android Logging system + for NDK (Native) applications and libraries. Interfaces for either + writing or reading logs. The log buffers are divided up in Main, Sys‐ + tem, Radio and Events sub-logs. + + The logging interfaces are a series of macros, all of which can be + overridden individually in order to control the verbosity of the appli‐ + cation or library. [ASR]LOG[VDIWE] calls are used to log to BAsic, + System or Radio sub-logs in either the Verbose, Debug, Info, Warning or + Error priorities. [ASR]LOG[VDIWE]_IF calls are used to perform thus + based on a condition being true. IF_ALOG[VDIWE] calls are true if the + current LOG_TAG is enabled at the specified priority. LOG_ALWAYS_FATAL + is used to ALOG a message, then kill the process. LOG_FATAL call is a + variant of LOG_ALWAYS_FATAL, only enabled in engineering, and not + release builds. ALOG_ASSERT is used to ALOG a message if the condition + is false; the condition is part of the logged message. + LOG_EVENT_(INT|LONG) is used to drop binary content into the Events + sub-log. + + The log reading interfaces permit opening the logs either singly or + multiply, retrieving a log entry at a time in time sorted order, + optionally limited to a specific pid and tail of the log(s) and finally + a call closing the logs. A single log can be opened with android_log‐ + ger_list_open; or multiple logs can be opened with android_log‐ + ger_list_alloc, calling in turn the android_logger_open for each log + id. Each entry can be retrieved with android_logger_list_read. The + log(s) can be closed with android_logger_list_free. The logs should be + opened with an O_RDONLY mode. O_NDELAY mode will report when the log + reading is done with an EAGAIN error return code, otherwise the + android_logger_list_read call will block for new entries. + + The value returned by android_logger_open can be used as a parameter to + the android_logger_clear function to empty the sub-log. It is recom‐ + mended to only open log O_WRONLY. + + The value returned by android_logger_open can be used as a parameter to + the android_logger_get_log_(size|readable_size|version) to retrieve the + sub-log maximum size, readable size and log buffer format protocol ver‐ + sion respectively. android_logger_get_id returns the id that was used + when opening the sub-log. It is recommended to open the log O_RDONLY + in these cases. + +SEE ALSO + syslogd(8) + + + + 17 Dec 2013 LIBLOG(3) diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c index 5283619..da83a85 100644 --- a/liblog/fake_log_device.c +++ b/liblog/fake_log_device.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2008-2014 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. @@ -19,6 +19,8 @@ * passed on to the underlying (fake) log device. When not in the * simulator, messages are printed to stderr. */ +#include "fake_log_device.h" + #include <log/logd.h> #include <stdlib.h> @@ -320,11 +322,11 @@ static const char* getPriorityString(int priority) * Make up something to replace it. */ static ssize_t fake_writev(int fd, const struct iovec *iov, int iovcnt) { - int result = 0; - struct iovec* end = iov + iovcnt; + ssize_t result = 0; + const struct iovec* end = iov + iovcnt; for (; iov < end; iov++) { - int w = write(fd, iov->iov_base, iov->iov_len); - if (w != iov->iov_len) { + ssize_t w = write(fd, iov->iov_base, iov->iov_len); + if (w != (ssize_t) iov->iov_len) { if (w < 0) return w; return result + w; diff --git a/liblog/fake_log_device.h b/liblog/fake_log_device.h new file mode 100644 index 0000000..9d168cd --- /dev/null +++ b/liblog/fake_log_device.h @@ -0,0 +1,28 @@ +/* + * 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. + */ + +#ifndef _LIBLOG_FAKE_LOG_DEVICE_H +#define _LIBLOG_FAKE_LOG_DEVICE_H + +#include <sys/types.h> + +struct iovec; + +int fakeLogOpen(const char *pathName, int flags); +int fakeLogClose(int fd); +ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count); + +#endif // _LIBLOG_FAKE_LOG_DEVICE_H diff --git a/liblog/log_read.c b/liblog/log_read.c new file mode 100644 index 0000000..47aa711 --- /dev/null +++ b/liblog/log_read.c @@ -0,0 +1,665 @@ +/* +** Copyright 2013-2014, 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 _GNU_SOURCE /* asprintf for x86 host */ +#include <errno.h> +#include <fcntl.h> +#include <poll.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <cutils/list.h> +#include <log/log.h> +#include <log/logger.h> + +#include <sys/ioctl.h> + +#define __LOGGERIO 0xAE + +#define LOGGER_GET_LOG_BUF_SIZE _IO(__LOGGERIO, 1) /* size of log */ +#define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* used log len */ +#define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* next entry len */ +#define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */ +#define LOGGER_GET_VERSION _IO(__LOGGERIO, 5) /* abi version */ +#define LOGGER_SET_VERSION _IO(__LOGGERIO, 6) /* abi version */ + +typedef char bool; +#define false (const bool)0 +#define true (const bool)1 + +#define LOG_FILE_DIR "/dev/log/" + +/* timeout in milliseconds */ +#define LOG_TIMEOUT_FLUSH 5 +#define LOG_TIMEOUT_NEVER -1 + +#define logger_for_each(logger, logger_list) \ + for (logger = node_to_item((logger_list)->node.next, struct logger, node); \ + logger != node_to_item(&(logger_list)->node, struct logger, node); \ + logger = node_to_item((logger)->node.next, struct logger, node)) + +/* In the future, we would like to make this list extensible */ +static const char *LOG_NAME[LOG_ID_MAX] = { + [LOG_ID_MAIN] = "main", + [LOG_ID_RADIO] = "radio", + [LOG_ID_EVENTS] = "events", + [LOG_ID_SYSTEM] = "system" +}; + +const char *android_log_id_to_name(log_id_t log_id) { + if (log_id >= LOG_ID_MAX) { + log_id = LOG_ID_MAIN; + } + return LOG_NAME[log_id]; +} + +static int accessmode(int mode) +{ + if ((mode & O_ACCMODE) == O_WRONLY) { + return W_OK; + } + if ((mode & O_ACCMODE) == O_RDWR) { + return R_OK | W_OK; + } + return R_OK; +} + +/* repeated fragment */ +static int check_allocate_accessible(char **n, const char *b, int mode) +{ + *n = NULL; + + if (!b) { + return -EINVAL; + } + + asprintf(n, LOG_FILE_DIR "%s", b); + if (!*n) { + return -1; + } + + return access(*n, accessmode(mode)); +} + +log_id_t android_name_to_log_id(const char *logName) { + const char *b; + char *n; + int ret; + + if (!logName) { + return -1; /* NB: log_id_t is unsigned */ + } + b = strrchr(logName, '/'); + if (!b) { + b = logName; + } else { + ++b; + } + + ret = check_allocate_accessible(&n, b, O_RDONLY); + free(n); + if (ret) { + return ret; + } + + for(ret = LOG_ID_MIN; ret < LOG_ID_MAX; ++ret) { + const char *l = LOG_NAME[ret]; + if (l && !strcmp(b, l)) { + return ret; + } + } + return -1; /* should never happen */ +} + +struct logger_list { + struct listnode node; + int mode; + unsigned int tail; + pid_t pid; + unsigned int queued_lines; + int timeout_ms; + int error; + bool flush; + bool valid_entry; /* valiant(?) effort to deal with memory starvation */ + struct log_msg entry; +}; + +struct log_list { + struct listnode node; + struct log_msg entry; /* Truncated to event->len() + 1 to save space */ +}; + +struct logger { + struct listnode node; + struct logger_list *top; + int fd; + log_id_t id; + short *revents; + struct listnode log_list; +}; + +/* android_logger_alloc unimplemented, no use case */ +/* android_logger_free not exported */ +static void android_logger_free(struct logger *logger) +{ + if (!logger) { + return; + } + + while (!list_empty(&logger->log_list)) { + struct log_list *entry = node_to_item( + list_head(&logger->log_list), struct log_list, node); + list_remove(&entry->node); + free(entry); + if (logger->top->queued_lines) { + logger->top->queued_lines--; + } + } + + if (logger->fd >= 0) { + close(logger->fd); + } + + list_remove(&logger->node); + + free(logger); +} + +log_id_t android_logger_get_id(struct logger *logger) +{ + return logger->id; +} + +/* worker for sending the command to the logger */ +static int logger_ioctl(struct logger *logger, int cmd, int mode) +{ + char *n; + int f, ret; + + if (!logger || !logger->top) { + return -EFAULT; + } + + if (((mode & O_ACCMODE) == O_RDWR) + || (((mode ^ logger->top->mode) & O_ACCMODE) == 0)) { + return ioctl(logger->fd, cmd); + } + + /* We go here if android_logger_list_open got mode wrong for this ioctl */ + ret = check_allocate_accessible(&n, android_log_id_to_name(logger->id), mode); + if (ret) { + free(n); + return ret; + } + + f = open(n, mode); + free(n); + if (f < 0) { + return f; + } + + ret = ioctl(f, cmd); + close (f); + + return ret; +} + +int android_logger_clear(struct logger *logger) +{ + return logger_ioctl(logger, LOGGER_FLUSH_LOG, O_WRONLY); +} + +/* returns the total size of the log's ring buffer */ +int android_logger_get_log_size(struct logger *logger) +{ + return logger_ioctl(logger, LOGGER_GET_LOG_BUF_SIZE, O_RDWR); +} + +/* + * returns the readable size of the log's ring buffer (that is, amount of the + * log consumed) + */ +int android_logger_get_log_readable_size(struct logger *logger) +{ + return logger_ioctl(logger, LOGGER_GET_LOG_LEN, O_RDONLY); +} + +/* + * returns the logger version + */ +int android_logger_get_log_version(struct logger *logger) +{ + int ret = logger_ioctl(logger, LOGGER_GET_VERSION, O_RDWR); + return (ret < 0) ? 1 : ret; +} + +struct logger_list *android_logger_list_alloc(int mode, + unsigned int tail, + pid_t pid) +{ + struct logger_list *logger_list; + + logger_list = calloc(1, sizeof(*logger_list)); + if (!logger_list) { + return NULL; + } + list_init(&logger_list->node); + logger_list->mode = mode; + logger_list->tail = tail; + logger_list->pid = pid; + return logger_list; +} + +/* android_logger_list_register unimplemented, no use case */ +/* android_logger_list_unregister unimplemented, no use case */ + +/* Open the named log and add it to the logger list */ +struct logger *android_logger_open(struct logger_list *logger_list, + log_id_t id) +{ + struct listnode *node; + struct logger *logger; + char *n; + + if (!logger_list || (id >= LOG_ID_MAX)) { + goto err; + } + + logger_for_each(logger, logger_list) { + if (logger->id == id) { + goto ok; + } + } + + logger = calloc(1, sizeof(*logger)); + if (!logger) { + goto err; + } + + if (check_allocate_accessible(&n, android_log_id_to_name(id), + logger_list->mode)) { + goto err_name; + } + + logger->fd = open(n, logger_list->mode); + if (logger->fd < 0) { + goto err_name; + } + + free(n); + logger->id = id; + list_init(&logger->log_list); + list_add_tail(&logger_list->node, &logger->node); + logger->top = logger_list; + logger_list->timeout_ms = LOG_TIMEOUT_FLUSH; + goto ok; + +err_name: + free(n); +err_logger: + free(logger); +err: + logger = NULL; +ok: + return logger; +} + +/* Open the single named log and make it part of a new logger list */ +struct logger_list *android_logger_list_open(log_id_t id, + int mode, + unsigned int tail, + pid_t pid) +{ + struct logger_list *logger_list = android_logger_list_alloc(mode, tail, pid); + if (!logger_list) { + return NULL; + } + + if (!android_logger_open(logger_list, id)) { + android_logger_list_free(logger_list); + return NULL; + } + + return logger_list; +} + +/* prevent memory starvation when backfilling */ +static unsigned int queue_threshold(struct logger_list *logger_list) +{ + return (logger_list->tail < 64) ? 64 : logger_list->tail; +} + +static bool low_queue(struct listnode *node) +{ + /* low is considered less than 2 */ + return list_head(node) == list_tail(node); +} + +/* Flush queues in sequential order, one at a time */ +static int android_logger_list_flush(struct logger_list *logger_list, + struct log_msg *log_msg) +{ + int ret = 0; + struct log_list *firstentry = NULL; + + while ((ret == 0) + && (logger_list->flush + || (logger_list->queued_lines > logger_list->tail))) { + struct logger *logger; + + /* Merge sort */ + bool at_least_one_is_low = false; + struct logger *firstlogger = NULL; + firstentry = NULL; + + logger_for_each(logger, logger_list) { + struct listnode *node; + struct log_list *oldest = NULL; + + /* kernel logger channels not necessarily time-sort order */ + list_for_each(node, &logger->log_list) { + struct log_list *entry = node_to_item(node, + struct log_list, node); + if (!oldest + || (entry->entry.entry.sec < oldest->entry.entry.sec) + || ((entry->entry.entry.sec == oldest->entry.entry.sec) + && (entry->entry.entry.nsec < oldest->entry.entry.nsec))) { + oldest = entry; + } + } + + if (!oldest) { + at_least_one_is_low = true; + continue; + } else if (low_queue(&logger->log_list)) { + at_least_one_is_low = true; + } + + if (!firstentry + || (oldest->entry.entry.sec < firstentry->entry.entry.sec) + || ((oldest->entry.entry.sec == firstentry->entry.entry.sec) + && (oldest->entry.entry.nsec < firstentry->entry.entry.nsec))) { + firstentry = oldest; + firstlogger = logger; + } + } + + if (!firstentry) { + break; + } + + /* when trimming list, tries to keep one entry behind in each bucket */ + if (!logger_list->flush + && at_least_one_is_low + && (logger_list->queued_lines < queue_threshold(logger_list))) { + break; + } + + /* within tail?, send! */ + if ((logger_list->tail == 0) + || (logger_list->queued_lines <= logger_list->tail)) { + ret = firstentry->entry.entry.hdr_size; + if (!ret) { + ret = sizeof(firstentry->entry.entry_v1); + } + ret += firstentry->entry.entry.len; + + memcpy(log_msg->buf, firstentry->entry.buf, ret + 1); + log_msg->extra.id = firstlogger->id; + } + + /* next entry */ + list_remove(&firstentry->node); + free(firstentry); + if (logger_list->queued_lines) { + logger_list->queued_lines--; + } + } + + /* Flushed the list, no longer in tail mode for continuing content */ + if (logger_list->flush && !firstentry) { + logger_list->tail = 0; + } + return ret; +} + +/* Read from the selected logs */ +int android_logger_list_read(struct logger_list *logger_list, + struct log_msg *log_msg) +{ + struct logger *logger; + nfds_t nfds; + struct pollfd *p, *pollfds = NULL; + int error = 0, ret = 0; + + memset(log_msg, 0, sizeof(struct log_msg)); + + if (!logger_list) { + return -ENODEV; + } + + if (!(accessmode(logger_list->mode) & R_OK)) { + logger_list->error = EPERM; + goto done; + } + + nfds = 0; + logger_for_each(logger, logger_list) { + ++nfds; + } + if (nfds <= 0) { + error = ENODEV; + goto done; + } + + /* Do we have anything to offer from the buffer or state? */ + if (logger_list->valid_entry) { /* implies we are also in a flush state */ + goto flush; + } + + ret = android_logger_list_flush(logger_list, log_msg); + if (ret) { + goto done; + } + + if (logger_list->error) { /* implies we are also in a flush state */ + goto done; + } + + /* Lets start grinding on metal */ + pollfds = calloc(nfds, sizeof(struct pollfd)); + if (!pollfds) { + error = ENOMEM; + goto flush; + } + + p = pollfds; + logger_for_each(logger, logger_list) { + p->fd = logger->fd; + p->events = POLLIN; + logger->revents = &p->revents; + ++p; + } + + while (!ret && !error) { + int result; + + /* If we oversleep it's ok, i.e. ignore EINTR. */ + result = TEMP_FAILURE_RETRY( + poll(pollfds, nfds, logger_list->timeout_ms)); + + if (result <= 0) { + if (result) { + error = errno; + } else if (logger_list->mode & O_NDELAY) { + error = EAGAIN; + } else { + logger_list->timeout_ms = LOG_TIMEOUT_NEVER; + } + + logger_list->flush = true; + goto try_flush; + } + + logger_list->timeout_ms = LOG_TIMEOUT_FLUSH; + + /* Anti starvation */ + if (!logger_list->flush + && (logger_list->queued_lines > (queue_threshold(logger_list) / 2))) { + /* Any queues with input pending that is low? */ + bool starving = false; + logger_for_each(logger, logger_list) { + if ((*(logger->revents) & POLLIN) + && low_queue(&logger->log_list)) { + starving = true; + break; + } + } + + /* pushback on any queues that are not low */ + if (starving) { + logger_for_each(logger, logger_list) { + if ((*(logger->revents) & POLLIN) + && !low_queue(&logger->log_list)) { + *(logger->revents) &= ~POLLIN; + } + } + } + } + + logger_for_each(logger, logger_list) { + unsigned int hdr_size; + struct log_list *entry; + + if (!(*(logger->revents) & POLLIN)) { + continue; + } + + memset(logger_list->entry.buf, 0, sizeof(struct log_msg)); + /* NOTE: driver guarantees we read exactly one full entry */ + result = read(logger->fd, logger_list->entry.buf, + LOGGER_ENTRY_MAX_LEN); + if (result <= 0) { + if (!result) { + error = EIO; + } else if (errno != EINTR) { + error = errno; + } + continue; + } + + if (logger_list->pid + && (logger_list->pid != logger_list->entry.entry.pid)) { + continue; + } + + hdr_size = logger_list->entry.entry.hdr_size; + if (!hdr_size) { + hdr_size = sizeof(logger_list->entry.entry_v1); + } + + if ((hdr_size > sizeof(struct log_msg)) + || (logger_list->entry.entry.len + > sizeof(logger_list->entry.buf) - hdr_size) + || (logger_list->entry.entry.len != result - hdr_size)) { + error = EINVAL; + continue; + } + + logger_list->entry.extra.id = logger->id; + + /* speedup: If not tail, and only one list, send directly */ + if (!logger_list->tail + && (list_head(&logger_list->node) + == list_tail(&logger_list->node))) { + ret = result; + memcpy(log_msg->buf, logger_list->entry.buf, result + 1); + log_msg->extra.id = logger->id; + break; + } + + entry = malloc(sizeof(*entry) - sizeof(entry->entry) + result + 1); + + if (!entry) { + logger_list->valid_entry = true; + error = ENOMEM; + break; + } + + logger_list->queued_lines++; + + memcpy(entry->entry.buf, logger_list->entry.buf, result); + entry->entry.buf[result] = '\0'; + list_add_tail(&logger->log_list, &entry->node); + } + + if (ret <= 0) { +try_flush: + ret = android_logger_list_flush(logger_list, log_msg); + } + } + + free(pollfds); + +flush: + if (error) { + logger_list->flush = true; + } + + if (ret <= 0) { + ret = android_logger_list_flush(logger_list, log_msg); + + if (!ret && logger_list->valid_entry) { + ret = logger_list->entry.entry.hdr_size; + if (!ret) { + ret = sizeof(logger_list->entry.entry_v1); + } + ret += logger_list->entry.entry.len; + + memcpy(log_msg->buf, logger_list->entry.buf, + sizeof(struct log_msg)); + logger_list->valid_entry = false; + } + } + +done: + if (logger_list->error) { + error = logger_list->error; + } + if (error) { + logger_list->error = error; + if (!ret) { + ret = -error; + } + } + return ret; +} + +/* Close all the logs */ +void android_logger_list_free(struct logger_list *logger_list) +{ + if (logger_list == NULL) { + return; + } + + while (!list_empty(&logger_list->node)) { + struct listnode *node = list_head(&logger_list->node); + struct logger *logger = node_to_item(node, struct logger, node); + android_logger_free(logger); + } + + free(logger_list); +} diff --git a/liblog/logd_write.c b/liblog/logd_write.c index fff7cc4..5766f8c 100644 --- a/liblog/logd_write.c +++ b/liblog/logd_write.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * Copyright (C) 2007-2014 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. @@ -31,10 +31,16 @@ #include <log/logd.h> #include <log/log.h> -#define LOG_BUF_SIZE 1024 +#define LOGGER_LOG_MAIN "log/main" +#define LOGGER_LOG_RADIO "log/radio" +#define LOGGER_LOG_EVENTS "log/events" +#define LOGGER_LOG_SYSTEM "log/system" + +#define LOG_BUF_SIZE 1024 #if FAKE_LOG_DEVICE // This will be defined when building for the host. +#include "fake_log_device.h" #define log_open(pathname, flags) fakeLogOpen(pathname, flags) #define log_writev(filedes, vector, count) fakeLogWritev(filedes, vector, count) #define log_close(filedes) fakeLogClose(filedes) @@ -50,6 +56,8 @@ static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_ static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER; #endif +#define UNUSED __attribute__((__unused__)) + static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1 }; /* @@ -72,7 +80,8 @@ int __android_log_dev_available(void) return (g_log_status == kLogAvailable); } -static int __write_to_log_null(log_id_t log_fd, struct iovec *vec, size_t nr) +static int __write_to_log_null(log_id_t log_fd UNUSED, struct iovec *vec UNUSED, + size_t nr UNUSED) { return -1; } @@ -236,7 +245,7 @@ int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fm } void __android_log_assert(const char *cond, const char *tag, - const char *fmt, ...) + const char *fmt, ...) { char buf[LOG_BUF_SIZE]; diff --git a/liblog/logprint.c b/liblog/logprint.c index 508c825..a7480d5 100644 --- a/liblog/logprint.c +++ b/liblog/logprint.c @@ -1,6 +1,6 @@ -/* //device/libs/cutils/logprint.c +/* ** -** Copyright 2006, The Android Open Source Project +** Copyright 2006-2014, 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. @@ -377,8 +377,13 @@ int android_log_processLogBuffer(struct logger_entry *buf, int msgEnd = -1; int i; + char *msg = buf->msg; + struct logger_entry_v2 *buf2 = (struct logger_entry_v2 *)buf; + if (buf2->hdr_size) { + msg = ((char *)buf2) + buf2->hdr_size; + } for (i = 1; i < buf->len; i++) { - if (buf->msg[i] == '\0') { + if (msg[i] == '\0') { if (msgStart == -1) { msgStart = i + 1; } else { @@ -395,12 +400,12 @@ int android_log_processLogBuffer(struct logger_entry *buf, if (msgEnd == -1) { // incoming message not null-terminated; force it msgEnd = buf->len - 1; - buf->msg[msgEnd] = '\0'; + msg[msgEnd] = '\0'; } - entry->priority = buf->msg[0]; - entry->tag = buf->msg + 1; - entry->message = buf->msg + msgStart; + entry->priority = msg[0]; + entry->tag = msg + 1; + entry->message = msg + msgStart; entry->messageLen = msgEnd - msgStart; return 0; @@ -614,6 +619,10 @@ int android_log_processBinaryLogBuffer(struct logger_entry *buf, * Pull the tag out. */ eventData = (const unsigned char*) buf->msg; + struct logger_entry_v2 *buf2 = (struct logger_entry_v2 *)buf; + if (buf2->hdr_size) { + eventData = ((unsigned char *)buf2) + buf2->hdr_size; + } inCount = buf->len; if (inCount < 4) return -1; diff --git a/liblog/tests/Android.mk b/liblog/tests/Android.mk new file mode 100644 index 0000000..db06cf7 --- /dev/null +++ b/liblog/tests/Android.mk @@ -0,0 +1,77 @@ +# +# Copyright (C) 2013-2014 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. +# + +LOCAL_PATH := $(call my-dir) + +# ----------------------------------------------------------------------------- +# Benchmarks. +# ----------------------------------------------------------------------------- + +test_module_prefix := liblog- +test_tags := tests + +benchmark_c_flags := \ + -Ibionic/tests \ + -Wall -Wextra \ + -Werror \ + -fno-builtin \ + -std=gnu++11 + +benchmark_src_files := \ + benchmark_main.cpp \ + liblog_benchmark.cpp \ + +# Build benchmarks for the device. Run with: +# adb shell liblog-benchmarks +include $(CLEAR_VARS) +LOCAL_MODULE := $(test_module_prefix)benchmarks +LOCAL_MODULE_TAGS := $(test_tags) +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk +LOCAL_CFLAGS += $(benchmark_c_flags) +LOCAL_SHARED_LIBRARIES += liblog libm +LOCAL_SRC_FILES := $(benchmark_src_files) +ifndef LOCAL_SDK_VERSION +LOCAL_C_INCLUDES += bionic bionic/libstdc++/include external/stlport/stlport +LOCAL_SHARED_LIBRARIES += libstlport +endif +LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE) +include $(BUILD_EXECUTABLE) + +# ----------------------------------------------------------------------------- +# Unit tests. +# ----------------------------------------------------------------------------- + +test_c_flags := \ + -fstack-protector-all \ + -g \ + -Wall -Wextra \ + -Werror \ + -fno-builtin \ + +test_src_files := \ + liblog_test.cpp \ + +# Build tests for the device (with .so). Run with: +# adb shell /data/nativetest/liblog-unit-tests/liblog-unit-tests +include $(CLEAR_VARS) +LOCAL_MODULE := $(test_module_prefix)unit-tests +LOCAL_MODULE_TAGS := $(test_tags) +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk +LOCAL_CFLAGS += $(test_c_flags) +LOCAL_LDLIBS := -lpthread +LOCAL_SHARED_LIBRARIES := liblog +LOCAL_SRC_FILES := $(test_src_files) +include $(BUILD_NATIVE_TEST) diff --git a/liblog/tests/benchmark.h b/liblog/tests/benchmark.h new file mode 100644 index 0000000..7f96e6d --- /dev/null +++ b/liblog/tests/benchmark.h @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2012-2014 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 <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <vector> + +#ifndef BIONIC_BENCHMARK_H_ +#define BIONIC_BENCHMARK_H_ + +namespace testing { + +class Benchmark; +template <typename T> class BenchmarkWantsArg; +template <typename T> class BenchmarkWithArg; + +void BenchmarkRegister(Benchmark* bm); +int PrettyPrintInt(char* str, int len, unsigned int arg); + +class Benchmark { + public: + Benchmark(const char* name, void (*fn)(int)) : name_(strdup(name)), fn_(fn) { + BenchmarkRegister(this); + } + Benchmark(const char* name) : name_(strdup(name)), fn_(NULL) {} + + virtual ~Benchmark() { + free(name_); + } + + const char* Name() { return name_; } + virtual const char* ArgName() { return NULL; } + virtual void RunFn(int iterations) { fn_(iterations); } + + protected: + char* name_; + + private: + void (*fn_)(int); +}; + +template <typename T> +class BenchmarkWantsArgBase : public Benchmark { + public: + BenchmarkWantsArgBase(const char* name, void (*fn)(int, T)) : Benchmark(name) { + fn_arg_ = fn; + } + + BenchmarkWantsArgBase<T>* Arg(const char* arg_name, T arg) { + BenchmarkRegister(new BenchmarkWithArg<T>(name_, fn_arg_, arg_name, arg)); + return this; + } + + protected: + virtual void RunFn(int) { printf("can't run arg benchmark %s without arg\n", Name()); } + void (*fn_arg_)(int, T); +}; + +template <typename T> +class BenchmarkWithArg : public BenchmarkWantsArg<T> { + public: + BenchmarkWithArg(const char* name, void (*fn)(int, T), const char* arg_name, T arg) : + BenchmarkWantsArg<T>(name, fn), arg_(arg) { + arg_name_ = strdup(arg_name); + } + + virtual ~BenchmarkWithArg() { + free(arg_name_); + } + + virtual const char* ArgName() { return arg_name_; } + + protected: + virtual void RunFn(int iterations) { BenchmarkWantsArg<T>::fn_arg_(iterations, arg_); } + + private: + T arg_; + char* arg_name_; +}; + +template <typename T> +class BenchmarkWantsArg : public BenchmarkWantsArgBase<T> { + public: + BenchmarkWantsArg<T>(const char* name, void (*fn)(int, T)) : + BenchmarkWantsArgBase<T>(name, fn) { } +}; + +template <> +class BenchmarkWantsArg<int> : public BenchmarkWantsArgBase<int> { + public: + BenchmarkWantsArg<int>(const char* name, void (*fn)(int, int)) : + BenchmarkWantsArgBase<int>(name, fn) { } + + BenchmarkWantsArg<int>* Arg(int arg) { + char arg_name[100]; + PrettyPrintInt(arg_name, sizeof(arg_name), arg); + BenchmarkRegister(new BenchmarkWithArg<int>(name_, fn_arg_, arg_name, arg)); + return this; + } +}; + +static inline Benchmark* BenchmarkFactory(const char* name, void (*fn)(int)) { + return new Benchmark(name, fn); +} + +template <typename T> +static inline BenchmarkWantsArg<T>* BenchmarkFactory(const char* name, void (*fn)(int, T)) { + return new BenchmarkWantsArg<T>(name, fn); +} + +} // namespace testing + +template <typename T> +static inline void BenchmarkAddArg(::testing::Benchmark* b, const char* name, T arg) { + ::testing::BenchmarkWantsArg<T>* ba; + ba = static_cast< ::testing::BenchmarkWantsArg<T>* >(b); + ba->Arg(name, arg); +} + +void SetBenchmarkBytesProcessed(uint64_t); +void ResetBenchmarkTiming(void); +void StopBenchmarkTiming(void); +void StartBenchmarkTiming(void); +void StartBenchmarkTiming(uint64_t); +void StopBenchmarkTiming(uint64_t); + +#define BENCHMARK(f) \ + static ::testing::Benchmark* _benchmark_##f __attribute__((unused)) = \ + (::testing::Benchmark*)::testing::BenchmarkFactory(#f, f) + +#endif // BIONIC_BENCHMARK_H_ diff --git a/liblog/tests/benchmark_main.cpp b/liblog/tests/benchmark_main.cpp new file mode 100644 index 0000000..02df460 --- /dev/null +++ b/liblog/tests/benchmark_main.cpp @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2012-2014 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 <benchmark.h> + +#include <regex.h> +#include <stdio.h> +#include <stdlib.h> + +#include <string> +#include <map> +#include <vector> + +static uint64_t gBytesProcessed; +static uint64_t gBenchmarkTotalTimeNs; +static uint64_t gBenchmarkTotalTimeNsSquared; +static uint64_t gBenchmarkNum; +static uint64_t gBenchmarkStartTimeNs; + +typedef std::vector< ::testing::Benchmark* > BenchmarkList; +static BenchmarkList* gBenchmarks; + +static int Round(int n) { + int base = 1; + while (base*10 < n) { + base *= 10; + } + if (n < 2*base) { + return 2*base; + } + if (n < 5*base) { + return 5*base; + } + return 10*base; +} + +static uint64_t NanoTime() { + struct timespec t; + t.tv_sec = t.tv_nsec = 0; + clock_gettime(CLOCK_MONOTONIC, &t); + return static_cast<uint64_t>(t.tv_sec) * 1000000000ULL + t.tv_nsec; +} + +namespace testing { + +int PrettyPrintInt(char* str, int len, unsigned int arg) +{ + if (arg >= (1<<30) && arg % (1<<30) == 0) { + return snprintf(str, len, "%uGi", arg/(1<<30)); + } else if (arg >= (1<<20) && arg % (1<<20) == 0) { + return snprintf(str, len, "%uMi", arg/(1<<20)); + } else if (arg >= (1<<10) && arg % (1<<10) == 0) { + return snprintf(str, len, "%uKi", arg/(1<<10)); + } else if (arg >= 1000000000 && arg % 1000000000 == 0) { + return snprintf(str, len, "%uG", arg/1000000000); + } else if (arg >= 1000000 && arg % 1000000 == 0) { + return snprintf(str, len, "%uM", arg/1000000); + } else if (arg >= 1000 && arg % 1000 == 0) { + return snprintf(str, len, "%uK", arg/1000); + } else { + return snprintf(str, len, "%u", arg); + } +} + +bool ShouldRun(Benchmark* b, int argc, char* argv[]) { + if (argc == 1) { + return true; // With no arguments, we run all benchmarks. + } + // Otherwise, we interpret each argument as a regular expression and + // see if any of our benchmarks match. + for (int i = 1; i < argc; i++) { + regex_t re; + if (regcomp(&re, argv[i], 0) != 0) { + fprintf(stderr, "couldn't compile \"%s\" as a regular expression!\n", argv[i]); + exit(EXIT_FAILURE); + } + int match = regexec(&re, b->Name(), 0, NULL, 0); + regfree(&re); + if (match != REG_NOMATCH) { + return true; + } + } + return false; +} + +void BenchmarkRegister(Benchmark* b) { + if (gBenchmarks == NULL) { + gBenchmarks = new BenchmarkList; + } + gBenchmarks->push_back(b); +} + +void RunRepeatedly(Benchmark* b, int iterations) { + gBytesProcessed = 0; + ResetBenchmarkTiming(); + uint64_t StartTimeNs = NanoTime(); + b->RunFn(iterations); + // Catch us if we fail to log anything. + if ((gBenchmarkTotalTimeNs == 0) + && (StartTimeNs != 0) + && (gBenchmarkStartTimeNs == 0)) { + gBenchmarkTotalTimeNs = NanoTime() - StartTimeNs; + } +} + +void Run(Benchmark* b) { + // run once in case it's expensive + unsigned iterations = 1; + uint64_t s = NanoTime(); + RunRepeatedly(b, iterations); + s = NanoTime() - s; + while (s < 2e9 && gBenchmarkTotalTimeNs < 1e9 && iterations < 1e9) { + unsigned last = iterations; + if (gBenchmarkTotalTimeNs/iterations == 0) { + iterations = 1e9; + } else { + iterations = 1e9 / (gBenchmarkTotalTimeNs/iterations); + } + iterations = std::max(last + 1, std::min(iterations + iterations/2, 100*last)); + iterations = Round(iterations); + s = NanoTime(); + RunRepeatedly(b, iterations); + s = NanoTime() - s; + } + + char throughput[100]; + throughput[0] = '\0'; + if (gBenchmarkTotalTimeNs > 0 && gBytesProcessed > 0) { + double mib_processed = static_cast<double>(gBytesProcessed)/1e6; + double seconds = static_cast<double>(gBenchmarkTotalTimeNs)/1e9; + snprintf(throughput, sizeof(throughput), " %8.2f MiB/s", mib_processed/seconds); + } + + char full_name[100]; + snprintf(full_name, sizeof(full_name), "%s%s%s", b->Name(), + b->ArgName() ? "/" : "", + b->ArgName() ? b->ArgName() : ""); + + uint64_t mean = gBenchmarkTotalTimeNs / iterations; + uint64_t sdev = 0; + if (gBenchmarkNum == iterations) { + mean = gBenchmarkTotalTimeNs / gBenchmarkNum; + uint64_t nXvariance = gBenchmarkTotalTimeNsSquared * gBenchmarkNum + - (gBenchmarkTotalTimeNs * gBenchmarkTotalTimeNs); + sdev = (sqrt((double)nXvariance) / gBenchmarkNum / gBenchmarkNum) + 0.5; + } + if (mean > (10000 * sdev)) { + printf("%-25s %10llu %10llu%s\n", full_name, + static_cast<uint64_t>(iterations), mean, throughput); + } else { + printf("%-25s %10llu %10llu(\317\203%llu)%s\n", full_name, + static_cast<uint64_t>(iterations), mean, sdev, throughput); + } + fflush(stdout); +} + +} // namespace testing + +void SetBenchmarkBytesProcessed(uint64_t x) { + gBytesProcessed = x; +} + +void ResetBenchmarkTiming() { + gBenchmarkStartTimeNs = 0; + gBenchmarkTotalTimeNs = 0; + gBenchmarkTotalTimeNsSquared = 0; + gBenchmarkNum = 0; +} + +void StopBenchmarkTiming(void) { + if (gBenchmarkStartTimeNs != 0) { + int64_t diff = NanoTime() - gBenchmarkStartTimeNs; + gBenchmarkTotalTimeNs += diff; + gBenchmarkTotalTimeNsSquared += diff * diff; + ++gBenchmarkNum; + } + gBenchmarkStartTimeNs = 0; +} + +void StartBenchmarkTiming(void) { + if (gBenchmarkStartTimeNs == 0) { + gBenchmarkStartTimeNs = NanoTime(); + } +} + +void StopBenchmarkTiming(uint64_t NanoTime) { + if (gBenchmarkStartTimeNs != 0) { + int64_t diff = NanoTime - gBenchmarkStartTimeNs; + gBenchmarkTotalTimeNs += diff; + gBenchmarkTotalTimeNsSquared += diff * diff; + if (NanoTime != 0) { + ++gBenchmarkNum; + } + } + gBenchmarkStartTimeNs = 0; +} + +void StartBenchmarkTiming(uint64_t NanoTime) { + if (gBenchmarkStartTimeNs == 0) { + gBenchmarkStartTimeNs = NanoTime; + } +} + +int main(int argc, char* argv[]) { + if (gBenchmarks->empty()) { + fprintf(stderr, "No benchmarks registered!\n"); + exit(EXIT_FAILURE); + } + + bool need_header = true; + for (auto b : *gBenchmarks) { + if (ShouldRun(b, argc, argv)) { + if (need_header) { + printf("%-25s %10s %10s\n", "", "iterations", "ns/op"); + fflush(stdout); + need_header = false; + } + Run(b); + } + } + + if (need_header) { + fprintf(stderr, "No matching benchmarks!\n"); + fprintf(stderr, "Available benchmarks:\n"); + for (auto b : *gBenchmarks) { + fprintf(stderr, " %s\n", b->Name()); + } + exit(EXIT_FAILURE); + } + + return 0; +} diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp new file mode 100644 index 0000000..19406fb --- /dev/null +++ b/liblog/tests/liblog_benchmark.cpp @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2013-2014 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 <sys/socket.h> +#include <cutils/sockets.h> +#include <log/log.h> +#include <log/logger.h> +#include <log/log_read.h> + +#include "benchmark.h" + +// enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and +// non-syscall libs. Since we are benchmarking, or using this in the emergency +// signal to stuff a terminating code, we do NOT want to introduce +// a syscall or usleep on EAGAIN retry. +#define LOG_FAILURE_RETRY(exp) ({ \ + typeof (exp) _rc; \ + do { \ + _rc = (exp); \ + } while (((_rc == -1) \ + && ((errno == EINTR) \ + || (errno == EAGAIN))) \ + || (_rc == -EINTR) \ + || (_rc == -EAGAIN)); \ + _rc; }) + +/* + * Measure the fastest rate we can reliabley stuff print messages into + * the log at high pressure. Expect this to be less than double the process + * wakeup time (2ms?) + */ +static void BM_log_maximum_retry(int iters) { + StartBenchmarkTiming(); + + for (int i = 0; i < iters; ++i) { + LOG_FAILURE_RETRY( + __android_log_print(ANDROID_LOG_INFO, + "BM_log_maximum_retry", "%d", i)); + } + + StopBenchmarkTiming(); +} +BENCHMARK(BM_log_maximum_retry); + +/* + * Measure the fastest rate we can stuff print messages into the log + * at high pressure. Expect this to be less than double the process wakeup + * time (2ms?) + */ +static void BM_log_maximum(int iters) { + StartBenchmarkTiming(); + + for (int i = 0; i < iters; ++i) { + __android_log_print(ANDROID_LOG_INFO, "BM_log_maximum", "%d", i); + } + + StopBenchmarkTiming(); +} +BENCHMARK(BM_log_maximum); + +/* + * Measure the time it takes to submit the android logging call using + * discrete acquisition under light load. Expect this to be a pair of + * syscall periods (2us). + */ +static void BM_clock_overhead(int iters) { + for (int i = 0; i < iters; ++i) { + StartBenchmarkTiming(); + StopBenchmarkTiming(); + } +} +BENCHMARK(BM_clock_overhead); + +/* + * Measure the time it takes to submit the android logging call using + * discrete acquisition under light load. Expect this to be a dozen or so + * syscall periods (40us). + */ +static void BM_log_overhead(int iters) { + for (int i = 0; i < iters; ++i) { + StartBenchmarkTiming(); + __android_log_print(ANDROID_LOG_INFO, "BM_log_overhead", "%d", i); + StopBenchmarkTiming(); + usleep(1000); + } +} +BENCHMARK(BM_log_overhead); + +static void caught_latency(int signum) +{ + unsigned long long v = 0xDEADBEEFA55A5AA5ULL; + + LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v))); +} + +static unsigned long long caught_convert(char *cp) +{ + unsigned long long l = cp[0] & 0xFF; + l |= (unsigned long long) (cp[1] & 0xFF) << 8; + l |= (unsigned long long) (cp[2] & 0xFF) << 16; + l |= (unsigned long long) (cp[3] & 0xFF) << 24; + l |= (unsigned long long) (cp[4] & 0xFF) << 32; + l |= (unsigned long long) (cp[5] & 0xFF) << 40; + l |= (unsigned long long) (cp[6] & 0xFF) << 48; + l |= (unsigned long long) (cp[7] & 0xFF) << 56; + return l; +} + +static const int alarm_time = 3; + +/* + * Measure the time it takes for the logd posting call to acquire the + * timestamp to place into the internal record. Expect this to be less than + * 4 syscalls (3us). + */ +static void BM_log_latency(int iters) { + pid_t pid = getpid(); + + struct logger_list * logger_list = android_logger_list_open(LOG_ID_EVENTS, + O_RDONLY, 0, pid); + + if (!logger_list) { + fprintf(stderr, "Unable to open events log: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + + signal(SIGALRM, caught_latency); + alarm(alarm_time); + + for (int j = 0, i = 0; i < iters && j < 10*iters; ++i, ++j) { + log_time ts; + LOG_FAILURE_RETRY(( + clock_gettime(CLOCK_REALTIME, &ts), + android_btWriteLog(0, EVENT_TYPE_LONG, &ts, sizeof(ts)))); + + for (;;) { + log_msg log_msg; + int ret = android_logger_list_read(logger_list, &log_msg); + alarm(alarm_time); + + if (ret <= 0) { + iters = i; + break; + } + if ((log_msg.entry.len != (4 + 1 + 8)) + || (log_msg.id() != LOG_ID_EVENTS)) { + continue; + } + + char* eventData = log_msg.msg(); + + if (eventData[4] != EVENT_TYPE_LONG) { + continue; + } + log_time tx(eventData + 4 + 1); + if (ts != tx) { + if (0xDEADBEEFA55A5AA5ULL == caught_convert(eventData + 4 + 1)) { + iters = i; + break; + } + continue; + } + + uint64_t start = ts.nsec(); + uint64_t end = log_msg.nsec(); + if (end >= start) { + StartBenchmarkTiming(start); + StopBenchmarkTiming(end); + } else { + --i; + } + break; + } + } + + signal(SIGALRM, SIG_DFL); + alarm(0); + + android_logger_list_free(logger_list); +} +BENCHMARK(BM_log_latency); + +static void caught_delay(int signum) +{ + unsigned long long v = 0xDEADBEEFA55A5AA6ULL; + + LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v))); +} + +/* + * Measure the time it takes for the logd posting call to make it into + * the logs. Expect this to be less than double the process wakeup time (2ms). + */ +static void BM_log_delay(int iters) { + pid_t pid = getpid(); + + struct logger_list * logger_list = android_logger_list_open(LOG_ID_EVENTS, + O_RDONLY, 0, pid); + + if (!logger_list) { + fprintf(stderr, "Unable to open events log: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + + signal(SIGALRM, caught_delay); + alarm(alarm_time); + + StartBenchmarkTiming(); + + for (int i = 0; i < iters; ++i) { + log_time ts(CLOCK_REALTIME); + + LOG_FAILURE_RETRY( + android_btWriteLog(0, EVENT_TYPE_LONG, &ts, sizeof(ts))); + + for (;;) { + log_msg log_msg; + int ret = android_logger_list_read(logger_list, &log_msg); + alarm(alarm_time); + + if (ret <= 0) { + iters = i; + break; + } + if ((log_msg.entry.len != (4 + 1 + 8)) + || (log_msg.id() != LOG_ID_EVENTS)) { + continue; + } + + char* eventData = log_msg.msg(); + + if (eventData[4] != EVENT_TYPE_LONG) { + continue; + } + log_time tx(eventData + 4 + 1); + if (ts != tx) { + if (0xDEADBEEFA55A5AA6ULL == caught_convert(eventData + 4 + 1)) { + iters = i; + break; + } + continue; + } + + break; + } + } + + signal(SIGALRM, SIG_DFL); + alarm(0); + + StopBenchmarkTiming(); + + android_logger_list_free(logger_list); +} +BENCHMARK(BM_log_delay); diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp new file mode 100644 index 0000000..9ae8f22 --- /dev/null +++ b/liblog/tests/liblog_test.cpp @@ -0,0 +1,319 @@ +/* + * Copyright (C) 2013-2014 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 <fcntl.h> +#include <signal.h> +#include <gtest/gtest.h> +#include <log/log.h> +#include <log/logger.h> +#include <log/log_read.h> + +// enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and +// non-syscall libs. Since we are only using this in the emergency of +// a signal to stuff a terminating code into the logs, we will spin rather +// than try a usleep. +#define LOG_FAILURE_RETRY(exp) ({ \ + typeof (exp) _rc; \ + do { \ + _rc = (exp); \ + } while (((_rc == -1) \ + && ((errno == EINTR) \ + || (errno == EAGAIN))) \ + || (_rc == -EINTR) \ + || (_rc == -EAGAIN)); \ + _rc; }) + +TEST(liblog, __android_log_buf_print) { + ASSERT_LT(0, __android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, + "TEST__android_log_buf_print", + "radio")); + usleep(1000); + ASSERT_LT(0, __android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, + "TEST__android_log_buf_print", + "system")); + usleep(1000); + ASSERT_LT(0, __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO, + "TEST__android_log_buf_print", + "main")); + usleep(1000); +} + +TEST(liblog, __android_log_buf_write) { + ASSERT_LT(0, __android_log_buf_write(LOG_ID_RADIO, ANDROID_LOG_INFO, + "TEST__android_log_buf_write", + "radio")); + usleep(1000); + ASSERT_LT(0, __android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO, + "TEST__android_log_buf_write", + "system")); + usleep(1000); + ASSERT_LT(0, __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO, + "TEST__android_log_buf_write", + "main")); + usleep(1000); +} + +TEST(liblog, __android_log_btwrite) { + int intBuf = 0xDEADBEEF; + ASSERT_LT(0, __android_log_btwrite(0, + EVENT_TYPE_INT, + &intBuf, sizeof(intBuf))); + long long longBuf = 0xDEADBEEFA55A5AA5; + ASSERT_LT(0, __android_log_btwrite(0, + EVENT_TYPE_LONG, + &longBuf, sizeof(longBuf))); + usleep(1000); + char Buf[] = "\20\0\0\0DeAdBeEfA55a5aA5"; + ASSERT_LT(0, __android_log_btwrite(0, + EVENT_TYPE_STRING, + Buf, sizeof(Buf) - 1)); + usleep(1000); +} + +static void* ConcurrentPrintFn(void *arg) { + int ret = __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO, + "TEST__android_log_print", "Concurrent %d", + reinterpret_cast<int>(arg)); + return reinterpret_cast<void*>(ret); +} + +#define NUM_CONCURRENT 64 +#define _concurrent_name(a,n) a##__concurrent##n +#define concurrent_name(a,n) _concurrent_name(a,n) + +TEST(liblog, concurrent_name(__android_log_buf_print, NUM_CONCURRENT)) { + pthread_t t[NUM_CONCURRENT]; + int i; + for (i=0; i < NUM_CONCURRENT; i++) { + ASSERT_EQ(0, pthread_create(&t[i], NULL, + ConcurrentPrintFn, + reinterpret_cast<void *>(i))); + } + int ret = 0; + for (i=0; i < NUM_CONCURRENT; i++) { + void* result; + ASSERT_EQ(0, pthread_join(t[i], &result)); + if ((0 == ret) && (0 != reinterpret_cast<int>(result))) { + ret = reinterpret_cast<int>(result); + } + } + ASSERT_LT(0, ret); +} + +TEST(liblog, __android_log_btwrite__android_logger_list_read) { + struct logger_list *logger_list; + + pid_t pid = getpid(); + + ASSERT_EQ(0, NULL == (logger_list = android_logger_list_open( + LOG_ID_EVENTS, O_RDONLY | O_NDELAY, 1000, pid))); + + log_time ts(CLOCK_MONOTONIC); + + ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts))); + usleep(1000000); + + int count = 0; + + for (;;) { + log_msg log_msg; + if (android_logger_list_read(logger_list, &log_msg) <= 0) { + break; + } + + ASSERT_EQ(log_msg.entry.pid, pid); + + if ((log_msg.entry.len != (4 + 1 + 8)) + || (log_msg.id() != LOG_ID_EVENTS)) { + continue; + } + + char *eventData = log_msg.msg(); + + if (eventData[4] != EVENT_TYPE_LONG) { + continue; + } + + log_time tx(eventData + 4 + 1); + if (ts == tx) { + ++count; + } + } + + ASSERT_EQ(1, count); + + android_logger_list_close(logger_list); +} + +static unsigned signaled; +log_time signal_time; + +static void caught_blocking(int signum) +{ + unsigned long long v = 0xDEADBEEFA55A0000ULL; + + v += getpid() & 0xFFFF; + + ++signaled; + if ((signal_time.tv_sec == 0) && (signal_time.tv_nsec == 0)) { + clock_gettime(CLOCK_MONOTONIC, &signal_time); + signal_time.tv_sec += 2; + } + + LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v))); +} + +// Fill in current process user and system time in 10ms increments +static void get_ticks(unsigned long long *uticks, unsigned long long *sticks) +{ + *uticks = *sticks = 0; + + pid_t pid = getpid(); + + char buffer[512]; + snprintf(buffer, sizeof(buffer), "/proc/%u/stat", pid); + + FILE *fp = fopen(buffer, "r"); + if (!fp) { + return; + } + + char *cp = fgets(buffer, sizeof(buffer), fp); + fclose(fp); + if (!cp) { + return; + } + + pid_t d; + char s[sizeof(buffer)]; + char c; + long long ll; + unsigned long long ull; + + if (15 != sscanf(buffer, + "%d %s %c %lld %lld %lld %lld %lld %llu %llu %llu %llu %llu %llu %llu ", + &d, s, &c, &ll, &ll, &ll, &ll, &ll, &ull, &ull, &ull, &ull, &ull, + uticks, sticks)) { + *uticks = *sticks = 0; + } +} + +TEST(liblog, android_logger_list_read__cpu) { + struct logger_list *logger_list; + unsigned long long v = 0xDEADBEEFA55A0000ULL; + + pid_t pid = getpid(); + + v += pid & 0xFFFF; + + ASSERT_EQ(0, NULL == (logger_list = android_logger_list_open( + LOG_ID_EVENTS, O_RDONLY, 1000, pid))); + + int count = 0; + + int signals = 0; + + unsigned long long uticks_start; + unsigned long long sticks_start; + get_ticks(&uticks_start, &sticks_start); + + const unsigned alarm_time = 10; + + memset(&signal_time, 0, sizeof(signal_time)); + + signal(SIGALRM, caught_blocking); + alarm(alarm_time); + + signaled = 0; + + do { + log_msg log_msg; + if (android_logger_list_read(logger_list, &log_msg) <= 0) { + break; + } + + alarm(alarm_time); + + ++count; + + ASSERT_EQ(log_msg.entry.pid, pid); + + if ((log_msg.entry.len != (4 + 1 + 8)) + || (log_msg.id() != LOG_ID_EVENTS)) { + continue; + } + + char *eventData = log_msg.msg(); + + if (eventData[4] != EVENT_TYPE_LONG) { + continue; + } + + unsigned long long l = eventData[4 + 1 + 0] & 0xFF; + l |= (unsigned long long) (eventData[4 + 1 + 1] & 0xFF) << 8; + l |= (unsigned long long) (eventData[4 + 1 + 2] & 0xFF) << 16; + l |= (unsigned long long) (eventData[4 + 1 + 3] & 0xFF) << 24; + l |= (unsigned long long) (eventData[4 + 1 + 4] & 0xFF) << 32; + l |= (unsigned long long) (eventData[4 + 1 + 5] & 0xFF) << 40; + l |= (unsigned long long) (eventData[4 + 1 + 6] & 0xFF) << 48; + l |= (unsigned long long) (eventData[4 + 1 + 7] & 0xFF) << 56; + + if (l == v) { + ++signals; + break; + } + } while (!signaled || ({log_time t(CLOCK_MONOTONIC); t < signal_time;})); + alarm(0); + signal(SIGALRM, SIG_DFL); + + ASSERT_LT(1, count); + + ASSERT_EQ(1, signals); + + android_logger_list_close(logger_list); + + unsigned long long uticks_end; + unsigned long long sticks_end; + get_ticks(&uticks_end, &sticks_end); + + // Less than 1% in either user or system time, or both + const unsigned long long one_percent_ticks = alarm_time; + unsigned long long user_ticks = uticks_end - uticks_start; + unsigned long long system_ticks = sticks_end - sticks_start; + ASSERT_GT(one_percent_ticks, user_ticks); + ASSERT_GT(one_percent_ticks, system_ticks); + ASSERT_GT(one_percent_ticks, user_ticks + system_ticks); +} + +TEST(liblog, android_logger_get_) { + struct logger_list * logger_list = android_logger_list_alloc(O_WRONLY, 0, 0); + + for(int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) { + log_id_t id = static_cast<log_id_t>(i); + const char *name = android_log_id_to_name(id); + if (id != android_name_to_log_id(name)) { + continue; + } + struct logger * logger; + ASSERT_EQ(0, NULL == (logger = android_logger_open(logger_list, id))); + ASSERT_EQ(id, android_logger_get_id(logger)); + ASSERT_LT(0, android_logger_get_log_size(logger)); + ASSERT_LT(0, android_logger_get_log_readable_size(logger)); + ASSERT_LT(0, android_logger_get_log_version(logger)); + } + + android_logger_list_close(logger_list); +} diff --git a/liblog/uio.c b/liblog/uio.c index cfa4cb1..24a6507 100644 --- a/liblog/uio.c +++ b/liblog/uio.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * Copyright (C) 2007-2014 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. @@ -24,8 +24,8 @@ int readv( int fd, struct iovec* vecs, int count ) int total = 0; for ( ; count > 0; count--, vecs++ ) { - const char* buf = vecs->iov_base; - int len = vecs->iov_len; + char* buf = vecs->iov_base; + int len = vecs->iov_len; while (len > 0) { int ret = read( fd, buf, len ); @@ -51,8 +51,8 @@ int writev( int fd, const struct iovec* vecs, int count ) int total = 0; for ( ; count > 0; count--, vecs++ ) { - const char* buf = (const char*)vecs->iov_base; - int len = (int)vecs->iov_len; + const char* buf = vecs->iov_base; + int len = vecs->iov_len; while (len > 0) { int ret = write( fd, buf, len ); diff --git a/libmemtrack/memtrack_test.c b/libmemtrack/memtrack_test.c index cd94bc5..eaadfa7 100644 --- a/libmemtrack/memtrack_test.c +++ b/libmemtrack/memtrack_test.c @@ -35,7 +35,7 @@ static int getprocname(pid_t pid, char *buf, int len) { return -1; } - if (asprintf(&filename, "/proc/%zd/cmdline", pid) < 0) { + if (asprintf(&filename, "/proc/%d/cmdline", pid) < 0) { rc = 1; goto exit; } diff --git a/libmincrypt/Android.mk b/libmincrypt/Android.mk index 090d0e5..7906986 100644 --- a/libmincrypt/Android.mk +++ b/libmincrypt/Android.mk @@ -1,18 +1,18 @@ # Copyright 2008 The Android Open Source Project # LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) +include $(CLEAR_VARS) LOCAL_MODULE := libmincrypt -LOCAL_SRC_FILES := rsa.c sha.c sha256.c +LOCAL_SRC_FILES := dsa_sig.c p256.c p256_ec.c p256_ecdsa.c rsa.c sha.c sha256.c +LOCAL_CFLAGS := -Wall -Werror include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) - LOCAL_MODULE := libmincrypt -LOCAL_SRC_FILES := rsa.c sha.c sha256.c +LOCAL_SRC_FILES := dsa_sig.c p256.c p256_ec.c p256_ecdsa.c rsa.c sha.c sha256.c +LOCAL_CFLAGS := -Wall -Werror include $(BUILD_HOST_STATIC_LIBRARY) - include $(LOCAL_PATH)/tools/Android.mk \ $(LOCAL_PATH)/test/Android.mk diff --git a/libmincrypt/dsa_sig.c b/libmincrypt/dsa_sig.c new file mode 100644 index 0000000..8df6cf7 --- /dev/null +++ b/libmincrypt/dsa_sig.c @@ -0,0 +1,125 @@ +/* + * Copyright 2013 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 <string.h> + +#include "mincrypt/p256.h" + +/** + * Trims off the leading zero bytes and copy it to a buffer aligning it to the end. + */ +static inline int trim_to_p256_bytes(unsigned char dst[P256_NBYTES], unsigned char *src, + int src_len) { + int dst_offset; + while (*src == '\0' && src_len > 0) { + src++; + src_len--; + } + if (src_len > P256_NBYTES || src_len < 1) { + return 0; + } + dst_offset = P256_NBYTES - src_len; + memset(dst, 0, dst_offset); + memcpy(dst + dst_offset, src, src_len); + return 1; +} + +/** + * Unpacks the ASN.1 DSA signature sequence. + */ +int dsa_sig_unpack(unsigned char* sig, int sig_len, p256_int* r_int, p256_int* s_int) { + /* + * Structure is: + * 0x30 0xNN SEQUENCE + s_length + * 0x02 0xNN INTEGER + r_length + * 0xAA 0xBB .. r_length bytes of "r" (offset 4) + * 0x02 0xNN INTEGER + s_length + * 0xMM 0xNN .. s_length bytes of "s" (offset 6 + r_len) + */ + int seq_len; + unsigned char r_bytes[P256_NBYTES]; + unsigned char s_bytes[P256_NBYTES]; + int r_len; + int s_len; + + memset(r_bytes, 0, sizeof(r_bytes)); + memset(s_bytes, 0, sizeof(s_bytes)); + + /* + * Must have at least: + * 2 bytes sequence header and length + * 2 bytes R integer header and length + * 1 byte of R + * 2 bytes S integer header and length + * 1 byte of S + * + * 8 bytes total + */ + if (sig_len < 8 || sig[0] != 0x30 || sig[2] != 0x02) { + return 0; + } + + seq_len = sig[1]; + if ((seq_len <= 0) || (seq_len + 2 != sig_len)) { + return 0; + } + + r_len = sig[3]; + /* + * Must have at least: + * 2 bytes for R header and length + * 2 bytes S integer header and length + * 1 byte of S + */ + if ((r_len < 1) || (r_len > seq_len - 5) || (sig[4 + r_len] != 0x02)) { + return 0; + } + s_len = sig[5 + r_len]; + + /** + * Must have: + * 2 bytes for R header and length + * r_len bytes for R + * 2 bytes S integer header and length + */ + if ((s_len < 1) || (s_len != seq_len - 4 - r_len)) { + return 0; + } + + /* + * ASN.1 encoded integers are zero-padded for positive integers. Make sure we have + * a correctly-sized buffer and that the resulting integer isn't too large. + */ + if (!trim_to_p256_bytes(r_bytes, &sig[4], r_len) + || !trim_to_p256_bytes(s_bytes, &sig[6 + r_len], s_len)) { + return 0; + } + + p256_from_bin(r_bytes, r_int); + p256_from_bin(s_bytes, s_int); + + return 1; +} diff --git a/libmincrypt/p256.c b/libmincrypt/p256.c new file mode 100644 index 0000000..1608d37 --- /dev/null +++ b/libmincrypt/p256.c @@ -0,0 +1,375 @@ +/* + * Copyright 2013 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. + */ + +// This is an implementation of the P256 elliptic curve group. It's written to +// be portable 32-bit, although it's still constant-time. +// +// WARNING: Implementing these functions in a constant-time manner is far from +// obvious. Be careful when touching this code. +// +// See http://www.imperialviolet.org/2010/12/04/ecc.html ([1]) for background. + +#include <assert.h> +#include <stdint.h> +#include <string.h> +#include <stdio.h> + +#include "mincrypt/p256.h" + +const p256_int SECP256r1_n = // curve order + {{0xfc632551, 0xf3b9cac2, 0xa7179e84, 0xbce6faad, -1, -1, 0, -1}}; + +const p256_int SECP256r1_p = // curve field size + {{-1, -1, -1, 0, 0, 0, 1, -1 }}; + +const p256_int SECP256r1_b = // curve b + {{0x27d2604b, 0x3bce3c3e, 0xcc53b0f6, 0x651d06b0, + 0x769886bc, 0xb3ebbd55, 0xaa3a93e7, 0x5ac635d8}}; + +static const p256_int p256_one = P256_ONE; + +void p256_init(p256_int* a) { + memset(a, 0, sizeof(*a)); +} + +void p256_clear(p256_int* a) { p256_init(a); } + +int p256_get_bit(const p256_int* scalar, int bit) { + return (P256_DIGIT(scalar, bit / P256_BITSPERDIGIT) + >> (bit & (P256_BITSPERDIGIT - 1))) & 1; +} + +int p256_is_zero(const p256_int* a) { + int i, result = 0; + for (i = 0; i < P256_NDIGITS; ++i) result |= P256_DIGIT(a, i); + return !result; +} + +// top, c[] += a[] * b +// Returns new top +static p256_digit mulAdd(const p256_int* a, + p256_digit b, + p256_digit top, + p256_digit* c) { + int i; + p256_ddigit carry = 0; + + for (i = 0; i < P256_NDIGITS; ++i) { + carry += *c; + carry += (p256_ddigit)P256_DIGIT(a, i) * b; + *c++ = (p256_digit)carry; + carry >>= P256_BITSPERDIGIT; + } + return top + (p256_digit)carry; +} + +// top, c[] -= top_a, a[] +static p256_digit subTop(p256_digit top_a, + const p256_digit* a, + p256_digit top_c, + p256_digit* c) { + int i; + p256_sddigit borrow = 0; + + for (i = 0; i < P256_NDIGITS; ++i) { + borrow += *c; + borrow -= *a++; + *c++ = (p256_digit)borrow; + borrow >>= P256_BITSPERDIGIT; + } + borrow += top_c; + borrow -= top_a; + top_c = (p256_digit)borrow; + assert((borrow >> P256_BITSPERDIGIT) == 0); + return top_c; +} + +// top, c[] -= MOD[] & mask (0 or -1) +// returns new top. +static p256_digit subM(const p256_int* MOD, + p256_digit top, + p256_digit* c, + p256_digit mask) { + int i; + p256_sddigit borrow = 0; + for (i = 0; i < P256_NDIGITS; ++i) { + borrow += *c; + borrow -= P256_DIGIT(MOD, i) & mask; + *c++ = (p256_digit)borrow; + borrow >>= P256_BITSPERDIGIT; + } + return top + (p256_digit)borrow; +} + +// top, c[] += MOD[] & mask (0 or -1) +// returns new top. +static p256_digit addM(const p256_int* MOD, + p256_digit top, + p256_digit* c, + p256_digit mask) { + int i; + p256_ddigit carry = 0; + for (i = 0; i < P256_NDIGITS; ++i) { + carry += *c; + carry += P256_DIGIT(MOD, i) & mask; + *c++ = (p256_digit)carry; + carry >>= P256_BITSPERDIGIT; + } + return top + (p256_digit)carry; +} + +// c = a * b mod MOD. c can be a and/or b. +void p256_modmul(const p256_int* MOD, + const p256_int* a, + const p256_digit top_b, + const p256_int* b, + p256_int* c) { + p256_digit tmp[P256_NDIGITS * 2 + 1] = { 0 }; + p256_digit top = 0; + int i; + + // Multiply/add into tmp. + for (i = 0; i < P256_NDIGITS; ++i) { + if (i) tmp[i + P256_NDIGITS - 1] = top; + top = mulAdd(a, P256_DIGIT(b, i), 0, tmp + i); + } + + // Multiply/add top digit + tmp[i + P256_NDIGITS - 1] = top; + top = mulAdd(a, top_b, 0, tmp + i); + + // Reduce tmp, digit by digit. + for (; i >= 0; --i) { + p256_digit reducer[P256_NDIGITS] = { 0 }; + p256_digit top_reducer; + + // top can be any value at this point. + // Guestimate reducer as top * MOD, since msw of MOD is -1. + top_reducer = mulAdd(MOD, top, 0, reducer); + + // Subtract reducer from top | tmp. + top = subTop(top_reducer, reducer, top, tmp + i); + + // top is now either 0 or 1. Make it 0, fixed-timing. + assert(top <= 1); + + top = subM(MOD, top, tmp + i, ~(top - 1)); + + assert(top == 0); + + // We have now reduced the top digit off tmp. Fetch new top digit. + top = tmp[i + P256_NDIGITS - 1]; + } + + // tmp might still be larger than MOD, yet same bit length. + // Make sure it is less, fixed-timing. + addM(MOD, 0, tmp, subM(MOD, 0, tmp, -1)); + + memcpy(c, tmp, P256_NBYTES); +} +int p256_is_odd(const p256_int* a) { return P256_DIGIT(a, 0) & 1; } +int p256_is_even(const p256_int* a) { return !(P256_DIGIT(a, 0) & 1); } + +p256_digit p256_shl(const p256_int* a, int n, p256_int* b) { + int i; + p256_digit top = P256_DIGIT(a, P256_NDIGITS - 1); + + n %= P256_BITSPERDIGIT; + for (i = P256_NDIGITS - 1; i > 0; --i) { + p256_digit accu = (P256_DIGIT(a, i) << n); + accu |= (P256_DIGIT(a, i - 1) >> (P256_BITSPERDIGIT - n)); + P256_DIGIT(b, i) = accu; + } + P256_DIGIT(b, i) = (P256_DIGIT(a, i) << n); + + top = (p256_digit)((((p256_ddigit)top) << n) >> P256_BITSPERDIGIT); + + return top; +} + +void p256_shr(const p256_int* a, int n, p256_int* b) { + int i; + + n %= P256_BITSPERDIGIT; + for (i = 0; i < P256_NDIGITS - 1; ++i) { + p256_digit accu = (P256_DIGIT(a, i) >> n); + accu |= (P256_DIGIT(a, i + 1) << (P256_BITSPERDIGIT - n)); + P256_DIGIT(b, i) = accu; + } + P256_DIGIT(b, i) = (P256_DIGIT(a, i) >> n); +} + +static void p256_shr1(const p256_int* a, int highbit, p256_int* b) { + int i; + + for (i = 0; i < P256_NDIGITS - 1; ++i) { + p256_digit accu = (P256_DIGIT(a, i) >> 1); + accu |= (P256_DIGIT(a, i + 1) << (P256_BITSPERDIGIT - 1)); + P256_DIGIT(b, i) = accu; + } + P256_DIGIT(b, i) = (P256_DIGIT(a, i) >> 1) | + (highbit << (P256_BITSPERDIGIT - 1)); +} + +// Return -1, 0, 1 for a < b, a == b or a > b respectively. +int p256_cmp(const p256_int* a, const p256_int* b) { + int i; + p256_sddigit borrow = 0; + p256_digit notzero = 0; + + for (i = 0; i < P256_NDIGITS; ++i) { + borrow += (p256_sddigit)P256_DIGIT(a, i) - P256_DIGIT(b, i); + // Track whether any result digit is ever not zero. + // Relies on !!(non-zero) evaluating to 1, e.g., !!(-1) evaluating to 1. + notzero |= !!((p256_digit)borrow); + borrow >>= P256_BITSPERDIGIT; + } + return (int)borrow | notzero; +} + +// c = a - b. Returns borrow: 0 or -1. +int p256_sub(const p256_int* a, const p256_int* b, p256_int* c) { + int i; + p256_sddigit borrow = 0; + + for (i = 0; i < P256_NDIGITS; ++i) { + borrow += (p256_sddigit)P256_DIGIT(a, i) - P256_DIGIT(b, i); + if (c) P256_DIGIT(c, i) = (p256_digit)borrow; + borrow >>= P256_BITSPERDIGIT; + } + return (int)borrow; +} + +// c = a + b. Returns carry: 0 or 1. +int p256_add(const p256_int* a, const p256_int* b, p256_int* c) { + int i; + p256_ddigit carry = 0; + + for (i = 0; i < P256_NDIGITS; ++i) { + carry += (p256_ddigit)P256_DIGIT(a, i) + P256_DIGIT(b, i); + if (c) P256_DIGIT(c, i) = (p256_digit)carry; + carry >>= P256_BITSPERDIGIT; + } + return (int)carry; +} + +// b = a + d. Returns carry, 0 or 1. +int p256_add_d(const p256_int* a, p256_digit d, p256_int* b) { + int i; + p256_ddigit carry = d; + + for (i = 0; i < P256_NDIGITS; ++i) { + carry += (p256_ddigit)P256_DIGIT(a, i); + if (b) P256_DIGIT(b, i) = (p256_digit)carry; + carry >>= P256_BITSPERDIGIT; + } + return (int)carry; +} + +// b = 1/a mod MOD, binary euclid. +void p256_modinv_vartime(const p256_int* MOD, + const p256_int* a, + p256_int* b) { + p256_int R = P256_ZERO; + p256_int S = P256_ONE; + p256_int U = *MOD; + p256_int V = *a; + + for (;;) { + if (p256_is_even(&U)) { + p256_shr1(&U, 0, &U); + if (p256_is_even(&R)) { + p256_shr1(&R, 0, &R); + } else { + // R = (R+MOD)/2 + p256_shr1(&R, p256_add(&R, MOD, &R), &R); + } + } else if (p256_is_even(&V)) { + p256_shr1(&V, 0, &V); + if (p256_is_even(&S)) { + p256_shr1(&S, 0, &S); + } else { + // S = (S+MOD)/2 + p256_shr1(&S, p256_add(&S, MOD, &S) , &S); + } + } else { // U,V both odd. + if (!p256_sub(&V, &U, NULL)) { + p256_sub(&V, &U, &V); + if (p256_sub(&S, &R, &S)) p256_add(&S, MOD, &S); + if (p256_is_zero(&V)) break; // done. + } else { + p256_sub(&U, &V, &U); + if (p256_sub(&R, &S, &R)) p256_add(&R, MOD, &R); + } + } + } + + p256_mod(MOD, &R, b); +} + +void p256_mod(const p256_int* MOD, + const p256_int* in, + p256_int* out) { + if (out != in) *out = *in; + addM(MOD, 0, P256_DIGITS(out), subM(MOD, 0, P256_DIGITS(out), -1)); +} + +// Verify y^2 == x^3 - 3x + b mod p +// and 0 < x < p and 0 < y < p +int p256_is_valid_point(const p256_int* x, const p256_int* y) { + p256_int y2, x3; + + if (p256_cmp(&SECP256r1_p, x) <= 0 || + p256_cmp(&SECP256r1_p, y) <= 0 || + p256_is_zero(x) || + p256_is_zero(y)) return 0; + + p256_modmul(&SECP256r1_p, y, 0, y, &y2); // y^2 + + p256_modmul(&SECP256r1_p, x, 0, x, &x3); // x^2 + p256_modmul(&SECP256r1_p, x, 0, &x3, &x3); // x^3 + if (p256_sub(&x3, x, &x3)) p256_add(&x3, &SECP256r1_p, &x3); // x^3 - x + if (p256_sub(&x3, x, &x3)) p256_add(&x3, &SECP256r1_p, &x3); // x^3 - 2x + if (p256_sub(&x3, x, &x3)) p256_add(&x3, &SECP256r1_p, &x3); // x^3 - 3x + if (p256_add(&x3, &SECP256r1_b, &x3)) // x^3 - 3x + b + p256_sub(&x3, &SECP256r1_p, &x3); + + return p256_cmp(&y2, &x3) == 0; +} + +void p256_from_bin(const uint8_t src[P256_NBYTES], p256_int* dst) { + int i; + const uint8_t* p = &src[0]; + + for (i = P256_NDIGITS - 1; i >= 0; --i) { + P256_DIGIT(dst, i) = + (p[0] << 24) | + (p[1] << 16) | + (p[2] << 8) | + p[3]; + p += 4; + } +} diff --git a/libmincrypt/p256_ec.c b/libmincrypt/p256_ec.c new file mode 100644 index 0000000..90262cc --- /dev/null +++ b/libmincrypt/p256_ec.c @@ -0,0 +1,1279 @@ +/* + * Copyright 2013 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. + */ + +// This is an implementation of the P256 elliptic curve group. It's written to +// be portable 32-bit, although it's still constant-time. +// +// WARNING: Implementing these functions in a constant-time manner is far from +// obvious. Be careful when touching this code. +// +// See http://www.imperialviolet.org/2010/12/04/ecc.html ([1]) for background. + +#include <stdint.h> +#include <stdio.h> + +#include <string.h> +#include <stdlib.h> + +#include "mincrypt/p256.h" + +typedef uint8_t u8; +typedef uint32_t u32; +typedef int32_t s32; +typedef uint64_t u64; + +/* Our field elements are represented as nine 32-bit limbs. + * + * The value of an felem (field element) is: + * x[0] + (x[1] * 2**29) + (x[2] * 2**57) + ... + (x[8] * 2**228) + * + * That is, each limb is alternately 29 or 28-bits wide in little-endian + * order. + * + * This means that an felem hits 2**257, rather than 2**256 as we would like. A + * 28, 29, ... pattern would cause us to hit 2**256, but that causes problems + * when multiplying as terms end up one bit short of a limb which would require + * much bit-shifting to correct. + * + * Finally, the values stored in an felem are in Montgomery form. So the value + * |y| is stored as (y*R) mod p, where p is the P-256 prime and R is 2**257. + */ +typedef u32 limb; +#define NLIMBS 9 +typedef limb felem[NLIMBS]; + +static const limb kBottom28Bits = 0xfffffff; +static const limb kBottom29Bits = 0x1fffffff; + +/* kOne is the number 1 as an felem. It's 2**257 mod p split up into 29 and + * 28-bit words. */ +static const felem kOne = { + 2, 0, 0, 0xffff800, + 0x1fffffff, 0xfffffff, 0x1fbfffff, 0x1ffffff, + 0 +}; +static const felem kZero = {0}; +static const felem kP = { + 0x1fffffff, 0xfffffff, 0x1fffffff, 0x3ff, + 0, 0, 0x200000, 0xf000000, + 0xfffffff +}; +static const felem k2P = { + 0x1ffffffe, 0xfffffff, 0x1fffffff, 0x7ff, + 0, 0, 0x400000, 0xe000000, + 0x1fffffff +}; +/* kPrecomputed contains precomputed values to aid the calculation of scalar + * multiples of the base point, G. It's actually two, equal length, tables + * concatenated. + * + * The first table contains (x,y) felem pairs for 16 multiples of the base + * point, G. + * + * Index | Index (binary) | Value + * 0 | 0000 | 0G (all zeros, omitted) + * 1 | 0001 | G + * 2 | 0010 | 2**64G + * 3 | 0011 | 2**64G + G + * 4 | 0100 | 2**128G + * 5 | 0101 | 2**128G + G + * 6 | 0110 | 2**128G + 2**64G + * 7 | 0111 | 2**128G + 2**64G + G + * 8 | 1000 | 2**192G + * 9 | 1001 | 2**192G + G + * 10 | 1010 | 2**192G + 2**64G + * 11 | 1011 | 2**192G + 2**64G + G + * 12 | 1100 | 2**192G + 2**128G + * 13 | 1101 | 2**192G + 2**128G + G + * 14 | 1110 | 2**192G + 2**128G + 2**64G + * 15 | 1111 | 2**192G + 2**128G + 2**64G + G + * + * The second table follows the same style, but the terms are 2**32G, + * 2**96G, 2**160G, 2**224G. + * + * This is ~2KB of data. */ +static const limb kPrecomputed[NLIMBS * 2 * 15 * 2] = { + 0x11522878, 0xe730d41, 0xdb60179, 0x4afe2ff, 0x12883add, 0xcaddd88, 0x119e7edc, 0xd4a6eab, 0x3120bee, + 0x1d2aac15, 0xf25357c, 0x19e45cdd, 0x5c721d0, 0x1992c5a5, 0xa237487, 0x154ba21, 0x14b10bb, 0xae3fe3, + 0xd41a576, 0x922fc51, 0x234994f, 0x60b60d3, 0x164586ae, 0xce95f18, 0x1fe49073, 0x3fa36cc, 0x5ebcd2c, + 0xb402f2f, 0x15c70bf, 0x1561925c, 0x5a26704, 0xda91e90, 0xcdc1c7f, 0x1ea12446, 0xe1ade1e, 0xec91f22, + 0x26f7778, 0x566847e, 0xa0bec9e, 0x234f453, 0x1a31f21a, 0xd85e75c, 0x56c7109, 0xa267a00, 0xb57c050, + 0x98fb57, 0xaa837cc, 0x60c0792, 0xcfa5e19, 0x61bab9e, 0x589e39b, 0xa324c5, 0x7d6dee7, 0x2976e4b, + 0x1fc4124a, 0xa8c244b, 0x1ce86762, 0xcd61c7e, 0x1831c8e0, 0x75774e1, 0x1d96a5a9, 0x843a649, 0xc3ab0fa, + 0x6e2e7d5, 0x7673a2a, 0x178b65e8, 0x4003e9b, 0x1a1f11c2, 0x7816ea, 0xf643e11, 0x58c43df, 0xf423fc2, + 0x19633ffa, 0x891f2b2, 0x123c231c, 0x46add8c, 0x54700dd, 0x59e2b17, 0x172db40f, 0x83e277d, 0xb0dd609, + 0xfd1da12, 0x35c6e52, 0x19ede20c, 0xd19e0c0, 0x97d0f40, 0xb015b19, 0x449e3f5, 0xe10c9e, 0x33ab581, + 0x56a67ab, 0x577734d, 0x1dddc062, 0xc57b10d, 0x149b39d, 0x26a9e7b, 0xc35df9f, 0x48764cd, 0x76dbcca, + 0xca4b366, 0xe9303ab, 0x1a7480e7, 0x57e9e81, 0x1e13eb50, 0xf466cf3, 0x6f16b20, 0x4ba3173, 0xc168c33, + 0x15cb5439, 0x6a38e11, 0x73658bd, 0xb29564f, 0x3f6dc5b, 0x53b97e, 0x1322c4c0, 0x65dd7ff, 0x3a1e4f6, + 0x14e614aa, 0x9246317, 0x1bc83aca, 0xad97eed, 0xd38ce4a, 0xf82b006, 0x341f077, 0xa6add89, 0x4894acd, + 0x9f162d5, 0xf8410ef, 0x1b266a56, 0xd7f223, 0x3e0cb92, 0xe39b672, 0x6a2901a, 0x69a8556, 0x7e7c0, + 0x9b7d8d3, 0x309a80, 0x1ad05f7f, 0xc2fb5dd, 0xcbfd41d, 0x9ceb638, 0x1051825c, 0xda0cf5b, 0x812e881, + 0x6f35669, 0x6a56f2c, 0x1df8d184, 0x345820, 0x1477d477, 0x1645db1, 0xbe80c51, 0xc22be3e, 0xe35e65a, + 0x1aeb7aa0, 0xc375315, 0xf67bc99, 0x7fdd7b9, 0x191fc1be, 0x61235d, 0x2c184e9, 0x1c5a839, 0x47a1e26, + 0xb7cb456, 0x93e225d, 0x14f3c6ed, 0xccc1ac9, 0x17fe37f3, 0x4988989, 0x1a90c502, 0x2f32042, 0xa17769b, + 0xafd8c7c, 0x8191c6e, 0x1dcdb237, 0x16200c0, 0x107b32a1, 0x66c08db, 0x10d06a02, 0x3fc93, 0x5620023, + 0x16722b27, 0x68b5c59, 0x270fcfc, 0xfad0ecc, 0xe5de1c2, 0xeab466b, 0x2fc513c, 0x407f75c, 0xbaab133, + 0x9705fe9, 0xb88b8e7, 0x734c993, 0x1e1ff8f, 0x19156970, 0xabd0f00, 0x10469ea7, 0x3293ac0, 0xcdc98aa, + 0x1d843fd, 0xe14bfe8, 0x15be825f, 0x8b5212, 0xeb3fb67, 0x81cbd29, 0xbc62f16, 0x2b6fcc7, 0xf5a4e29, + 0x13560b66, 0xc0b6ac2, 0x51ae690, 0xd41e271, 0xf3e9bd4, 0x1d70aab, 0x1029f72, 0x73e1c35, 0xee70fbc, + 0xad81baf, 0x9ecc49a, 0x86c741e, 0xfe6be30, 0x176752e7, 0x23d416, 0x1f83de85, 0x27de188, 0x66f70b8, + 0x181cd51f, 0x96b6e4c, 0x188f2335, 0xa5df759, 0x17a77eb6, 0xfeb0e73, 0x154ae914, 0x2f3ec51, 0x3826b59, + 0xb91f17d, 0x1c72949, 0x1362bf0a, 0xe23fddf, 0xa5614b0, 0xf7d8f, 0x79061, 0x823d9d2, 0x8213f39, + 0x1128ae0b, 0xd095d05, 0xb85c0c2, 0x1ecb2ef, 0x24ddc84, 0xe35e901, 0x18411a4a, 0xf5ddc3d, 0x3786689, + 0x52260e8, 0x5ae3564, 0x542b10d, 0x8d93a45, 0x19952aa4, 0x996cc41, 0x1051a729, 0x4be3499, 0x52b23aa, + 0x109f307e, 0x6f5b6bb, 0x1f84e1e7, 0x77a0cfa, 0x10c4df3f, 0x25a02ea, 0xb048035, 0xe31de66, 0xc6ecaa3, + 0x28ea335, 0x2886024, 0x1372f020, 0xf55d35, 0x15e4684c, 0xf2a9e17, 0x1a4a7529, 0xcb7beb1, 0xb2a78a1, + 0x1ab21f1f, 0x6361ccf, 0x6c9179d, 0xb135627, 0x1267b974, 0x4408bad, 0x1cbff658, 0xe3d6511, 0xc7d76f, + 0x1cc7a69, 0xe7ee31b, 0x54fab4f, 0x2b914f, 0x1ad27a30, 0xcd3579e, 0xc50124c, 0x50daa90, 0xb13f72, + 0xb06aa75, 0x70f5cc6, 0x1649e5aa, 0x84a5312, 0x329043c, 0x41c4011, 0x13d32411, 0xb04a838, 0xd760d2d, + 0x1713b532, 0xbaa0c03, 0x84022ab, 0x6bcf5c1, 0x2f45379, 0x18ae070, 0x18c9e11e, 0x20bca9a, 0x66f496b, + 0x3eef294, 0x67500d2, 0xd7f613c, 0x2dbbeb, 0xb741038, 0xe04133f, 0x1582968d, 0xbe985f7, 0x1acbc1a, + 0x1a6a939f, 0x33e50f6, 0xd665ed4, 0xb4b7bd6, 0x1e5a3799, 0x6b33847, 0x17fa56ff, 0x65ef930, 0x21dc4a, + 0x2b37659, 0x450fe17, 0xb357b65, 0xdf5efac, 0x15397bef, 0x9d35a7f, 0x112ac15f, 0x624e62e, 0xa90ae2f, + 0x107eecd2, 0x1f69bbe, 0x77d6bce, 0x5741394, 0x13c684fc, 0x950c910, 0x725522b, 0xdc78583, 0x40eeabb, + 0x1fde328a, 0xbd61d96, 0xd28c387, 0x9e77d89, 0x12550c40, 0x759cb7d, 0x367ef34, 0xae2a960, 0x91b8bdc, + 0x93462a9, 0xf469ef, 0xb2e9aef, 0xd2ca771, 0x54e1f42, 0x7aaa49, 0x6316abb, 0x2413c8e, 0x5425bf9, + 0x1bed3e3a, 0xf272274, 0x1f5e7326, 0x6416517, 0xea27072, 0x9cedea7, 0x6e7633, 0x7c91952, 0xd806dce, + 0x8e2a7e1, 0xe421e1a, 0x418c9e1, 0x1dbc890, 0x1b395c36, 0xa1dc175, 0x1dc4ef73, 0x8956f34, 0xe4b5cf2, + 0x1b0d3a18, 0x3194a36, 0x6c2641f, 0xe44124c, 0xa2f4eaa, 0xa8c25ba, 0xf927ed7, 0x627b614, 0x7371cca, + 0xba16694, 0x417bc03, 0x7c0a7e3, 0x9c35c19, 0x1168a205, 0x8b6b00d, 0x10e3edc9, 0x9c19bf2, 0x5882229, + 0x1b2b4162, 0xa5cef1a, 0x1543622b, 0x9bd433e, 0x364e04d, 0x7480792, 0x5c9b5b3, 0xe85ff25, 0x408ef57, + 0x1814cfa4, 0x121b41b, 0xd248a0f, 0x3b05222, 0x39bb16a, 0xc75966d, 0xa038113, 0xa4a1769, 0x11fbc6c, + 0x917e50e, 0xeec3da8, 0x169d6eac, 0x10c1699, 0xa416153, 0xf724912, 0x15cd60b7, 0x4acbad9, 0x5efc5fa, + 0xf150ed7, 0x122b51, 0x1104b40a, 0xcb7f442, 0xfbb28ff, 0x6ac53ca, 0x196142cc, 0x7bf0fa9, 0x957651, + 0x4e0f215, 0xed439f8, 0x3f46bd5, 0x5ace82f, 0x110916b6, 0x6db078, 0xffd7d57, 0xf2ecaac, 0xca86dec, + 0x15d6b2da, 0x965ecc9, 0x1c92b4c2, 0x1f3811, 0x1cb080f5, 0x2d8b804, 0x19d1c12d, 0xf20bd46, 0x1951fa7, + 0xa3656c3, 0x523a425, 0xfcd0692, 0xd44ddc8, 0x131f0f5b, 0xaf80e4a, 0xcd9fc74, 0x99bb618, 0x2db944c, + 0xa673090, 0x1c210e1, 0x178c8d23, 0x1474383, 0x10b8743d, 0x985a55b, 0x2e74779, 0x576138, 0x9587927, + 0x133130fa, 0xbe05516, 0x9f4d619, 0xbb62570, 0x99ec591, 0xd9468fe, 0x1d07782d, 0xfc72e0b, 0x701b298, + 0x1863863b, 0x85954b8, 0x121a0c36, 0x9e7fedf, 0xf64b429, 0x9b9d71e, 0x14e2f5d8, 0xf858d3a, 0x942eea8, + 0xda5b765, 0x6edafff, 0xa9d18cc, 0xc65e4ba, 0x1c747e86, 0xe4ea915, 0x1981d7a1, 0x8395659, 0x52ed4e2, + 0x87d43b7, 0x37ab11b, 0x19d292ce, 0xf8d4692, 0x18c3053f, 0x8863e13, 0x4c146c0, 0x6bdf55a, 0x4e4457d, + 0x16152289, 0xac78ec2, 0x1a59c5a2, 0x2028b97, 0x71c2d01, 0x295851f, 0x404747b, 0x878558d, 0x7d29aa4, + 0x13d8341f, 0x8daefd7, 0x139c972d, 0x6b7ea75, 0xd4a9dde, 0xff163d8, 0x81d55d7, 0xa5bef68, 0xb7b30d8, + 0xbe73d6f, 0xaa88141, 0xd976c81, 0x7e7a9cc, 0x18beb771, 0xd773cbd, 0x13f51951, 0x9d0c177, 0x1c49a78, +}; + + +/* Field element operations: */ + +/* NON_ZERO_TO_ALL_ONES returns: + * 0xffffffff for 0 < x <= 2**31 + * 0 for x == 0 or x > 2**31. + * + * x must be a u32 or an equivalent type such as limb. */ +#define NON_ZERO_TO_ALL_ONES(x) ((((u32)(x) - 1) >> 31) - 1) + +/* felem_reduce_carry adds a multiple of p in order to cancel |carry|, + * which is a term at 2**257. + * + * On entry: carry < 2**3, inout[0,2,...] < 2**29, inout[1,3,...] < 2**28. + * On exit: inout[0,2,..] < 2**30, inout[1,3,...] < 2**29. */ +static void felem_reduce_carry(felem inout, limb carry) { + const u32 carry_mask = NON_ZERO_TO_ALL_ONES(carry); + + inout[0] += carry << 1; + inout[3] += 0x10000000 & carry_mask; + /* carry < 2**3 thus (carry << 11) < 2**14 and we added 2**28 in the + * previous line therefore this doesn't underflow. */ + inout[3] -= carry << 11; + inout[4] += (0x20000000 - 1) & carry_mask; + inout[5] += (0x10000000 - 1) & carry_mask; + inout[6] += (0x20000000 - 1) & carry_mask; + inout[6] -= carry << 22; + /* This may underflow if carry is non-zero but, if so, we'll fix it in the + * next line. */ + inout[7] -= 1 & carry_mask; + inout[7] += carry << 25; +} + +/* felem_sum sets out = in+in2. + * + * On entry, in[i]+in2[i] must not overflow a 32-bit word. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29 */ +static void felem_sum(felem out, const felem in, const felem in2) { + limb carry = 0; + unsigned i; + + for (i = 0;; i++) { + out[i] = in[i] + in2[i]; + out[i] += carry; + carry = out[i] >> 29; + out[i] &= kBottom29Bits; + + i++; + if (i == NLIMBS) + break; + + out[i] = in[i] + in2[i]; + out[i] += carry; + carry = out[i] >> 28; + out[i] &= kBottom28Bits; + } + + felem_reduce_carry(out, carry); +} + +#define two31m3 (((limb)1) << 31) - (((limb)1) << 3) +#define two30m2 (((limb)1) << 30) - (((limb)1) << 2) +#define two30p13m2 (((limb)1) << 30) + (((limb)1) << 13) - (((limb)1) << 2) +#define two31m2 (((limb)1) << 31) - (((limb)1) << 2) +#define two31p24m2 (((limb)1) << 31) + (((limb)1) << 24) - (((limb)1) << 2) +#define two30m27m2 (((limb)1) << 30) - (((limb)1) << 27) - (((limb)1) << 2) + +/* zero31 is 0 mod p. */ +static const felem zero31 = { two31m3, two30m2, two31m2, two30p13m2, two31m2, two30m2, two31p24m2, two30m27m2, two31m2 }; + +/* felem_diff sets out = in-in2. + * + * On entry: in[0,2,...] < 2**30, in[1,3,...] < 2**29 and + * in2[0,2,...] < 2**30, in2[1,3,...] < 2**29. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */ +static void felem_diff(felem out, const felem in, const felem in2) { + limb carry = 0; + unsigned i; + + for (i = 0;; i++) { + out[i] = in[i] - in2[i]; + out[i] += zero31[i]; + out[i] += carry; + carry = out[i] >> 29; + out[i] &= kBottom29Bits; + + i++; + if (i == NLIMBS) + break; + + out[i] = in[i] - in2[i]; + out[i] += zero31[i]; + out[i] += carry; + carry = out[i] >> 28; + out[i] &= kBottom28Bits; + } + + felem_reduce_carry(out, carry); +} + +/* felem_reduce_degree sets out = tmp/R mod p where tmp contains 64-bit words + * with the same 29,28,... bit positions as an felem. + * + * The values in felems are in Montgomery form: x*R mod p where R = 2**257. + * Since we just multiplied two Montgomery values together, the result is + * x*y*R*R mod p. We wish to divide by R in order for the result also to be + * in Montgomery form. + * + * On entry: tmp[i] < 2**64 + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29 */ +static void felem_reduce_degree(felem out, u64 tmp[17]) { + /* The following table may be helpful when reading this code: + * + * Limb number: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10... + * Width (bits): 29| 28| 29| 28| 29| 28| 29| 28| 29| 28| 29 + * Start bit: 0 | 29| 57| 86|114|143|171|200|228|257|285 + * (odd phase): 0 | 28| 57| 85|114|142|171|199|228|256|285 */ + limb tmp2[18], carry, x, xMask; + unsigned i; + + /* tmp contains 64-bit words with the same 29,28,29-bit positions as an + * felem. So the top of an element of tmp might overlap with another + * element two positions down. The following loop eliminates this + * overlap. */ + tmp2[0] = (limb)(tmp[0] & kBottom29Bits); + + /* In the following we use "(limb) tmp[x]" and "(limb) (tmp[x]>>32)" to try + * and hint to the compiler that it can do a single-word shift by selecting + * the right register rather than doing a double-word shift and truncating + * afterwards. */ + tmp2[1] = ((limb) tmp[0]) >> 29; + tmp2[1] |= (((limb)(tmp[0] >> 32)) << 3) & kBottom28Bits; + tmp2[1] += ((limb) tmp[1]) & kBottom28Bits; + carry = tmp2[1] >> 28; + tmp2[1] &= kBottom28Bits; + + for (i = 2; i < 17; i++) { + tmp2[i] = ((limb)(tmp[i - 2] >> 32)) >> 25; + tmp2[i] += ((limb)(tmp[i - 1])) >> 28; + tmp2[i] += (((limb)(tmp[i - 1] >> 32)) << 4) & kBottom29Bits; + tmp2[i] += ((limb) tmp[i]) & kBottom29Bits; + tmp2[i] += carry; + carry = tmp2[i] >> 29; + tmp2[i] &= kBottom29Bits; + + i++; + if (i == 17) + break; + tmp2[i] = ((limb)(tmp[i - 2] >> 32)) >> 25; + tmp2[i] += ((limb)(tmp[i - 1])) >> 29; + tmp2[i] += (((limb)(tmp[i - 1] >> 32)) << 3) & kBottom28Bits; + tmp2[i] += ((limb) tmp[i]) & kBottom28Bits; + tmp2[i] += carry; + carry = tmp2[i] >> 28; + tmp2[i] &= kBottom28Bits; + } + + tmp2[17] = ((limb)(tmp[15] >> 32)) >> 25; + tmp2[17] += ((limb)(tmp[16])) >> 29; + tmp2[17] += (((limb)(tmp[16] >> 32)) << 3); + tmp2[17] += carry; + + /* Montgomery elimination of terms. + * + * Since R is 2**257, we can divide by R with a bitwise shift if we can + * ensure that the right-most 257 bits are all zero. We can make that true by + * adding multiplies of p without affecting the value. + * + * So we eliminate limbs from right to left. Since the bottom 29 bits of p + * are all ones, then by adding tmp2[0]*p to tmp2 we'll make tmp2[0] == 0. + * We can do that for 8 further limbs and then right shift to eliminate the + * extra factor of R. */ + for (i = 0;; i += 2) { + tmp2[i + 1] += tmp2[i] >> 29; + x = tmp2[i] & kBottom29Bits; + xMask = NON_ZERO_TO_ALL_ONES(x); + tmp2[i] = 0; + + /* The bounds calculations for this loop are tricky. Each iteration of + * the loop eliminates two words by adding values to words to their + * right. + * + * The following table contains the amounts added to each word (as an + * offset from the value of i at the top of the loop). The amounts are + * accounted for from the first and second half of the loop separately + * and are written as, for example, 28 to mean a value <2**28. + * + * Word: 3 4 5 6 7 8 9 10 + * Added in top half: 28 11 29 21 29 28 + * 28 29 + * 29 + * Added in bottom half: 29 10 28 21 28 28 + * 29 + * + * The value that is currently offset 7 will be offset 5 for the next + * iteration and then offset 3 for the iteration after that. Therefore + * the total value added will be the values added at 7, 5 and 3. + * + * The following table accumulates these values. The sums at the bottom + * are written as, for example, 29+28, to mean a value < 2**29+2**28. + * + * Word: 3 4 5 6 7 8 9 10 11 12 13 + * 28 11 10 29 21 29 28 28 28 28 28 + * 29 28 11 28 29 28 29 28 29 28 + * 29 28 21 21 29 21 29 21 + * 10 29 28 21 28 21 28 + * 28 29 28 29 28 29 28 + * 11 10 29 10 29 10 + * 29 28 11 28 11 + * 29 29 + * -------------------------------------------- + * 30+ 31+ 30+ 31+ 30+ + * 28+ 29+ 28+ 29+ 21+ + * 21+ 28+ 21+ 28+ 10 + * 10 21+ 10 21+ + * 11 11 + * + * So the greatest amount is added to tmp2[10] and tmp2[12]. If + * tmp2[10/12] has an initial value of <2**29, then the maximum value + * will be < 2**31 + 2**30 + 2**28 + 2**21 + 2**11, which is < 2**32, + * as required. */ + tmp2[i + 3] += (x << 10) & kBottom28Bits; + tmp2[i + 4] += (x >> 18); + + tmp2[i + 6] += (x << 21) & kBottom29Bits; + tmp2[i + 7] += x >> 8; + + /* At position 200, which is the starting bit position for word 7, we + * have a factor of 0xf000000 = 2**28 - 2**24. */ + tmp2[i + 7] += 0x10000000 & xMask; + /* Word 7 is 28 bits wide, so the 2**28 term exactly hits word 8. */ + tmp2[i + 8] += (x - 1) & xMask; + tmp2[i + 7] -= (x << 24) & kBottom28Bits; + tmp2[i + 8] -= x >> 4; + + tmp2[i + 8] += 0x20000000 & xMask; + tmp2[i + 8] -= x; + tmp2[i + 8] += (x << 28) & kBottom29Bits; + tmp2[i + 9] += ((x >> 1) - 1) & xMask; + + if (i+1 == NLIMBS) + break; + tmp2[i + 2] += tmp2[i + 1] >> 28; + x = tmp2[i + 1] & kBottom28Bits; + xMask = NON_ZERO_TO_ALL_ONES(x); + tmp2[i + 1] = 0; + + tmp2[i + 4] += (x << 11) & kBottom29Bits; + tmp2[i + 5] += (x >> 18); + + tmp2[i + 7] += (x << 21) & kBottom28Bits; + tmp2[i + 8] += x >> 7; + + /* At position 199, which is the starting bit of the 8th word when + * dealing with a context starting on an odd word, we have a factor of + * 0x1e000000 = 2**29 - 2**25. Since we have not updated i, the 8th + * word from i+1 is i+8. */ + tmp2[i + 8] += 0x20000000 & xMask; + tmp2[i + 9] += (x - 1) & xMask; + tmp2[i + 8] -= (x << 25) & kBottom29Bits; + tmp2[i + 9] -= x >> 4; + + tmp2[i + 9] += 0x10000000 & xMask; + tmp2[i + 9] -= x; + tmp2[i + 10] += (x - 1) & xMask; + } + + /* We merge the right shift with a carry chain. The words above 2**257 have + * widths of 28,29,... which we need to correct when copying them down. */ + carry = 0; + for (i = 0; i < 8; i++) { + /* The maximum value of tmp2[i + 9] occurs on the first iteration and + * is < 2**30+2**29+2**28. Adding 2**29 (from tmp2[i + 10]) is + * therefore safe. */ + out[i] = tmp2[i + 9]; + out[i] += carry; + out[i] += (tmp2[i + 10] << 28) & kBottom29Bits; + carry = out[i] >> 29; + out[i] &= kBottom29Bits; + + i++; + out[i] = tmp2[i + 9] >> 1; + out[i] += carry; + carry = out[i] >> 28; + out[i] &= kBottom28Bits; + } + + out[8] = tmp2[17]; + out[8] += carry; + carry = out[8] >> 29; + out[8] &= kBottom29Bits; + + felem_reduce_carry(out, carry); +} + +/* felem_square sets out=in*in. + * + * On entry: in[0,2,...] < 2**30, in[1,3,...] < 2**29. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */ +static void felem_square(felem out, const felem in) { + u64 tmp[17]; + + tmp[0] = ((u64) in[0]) * in[0]; + tmp[1] = ((u64) in[0]) * (in[1] << 1); + tmp[2] = ((u64) in[0]) * (in[2] << 1) + + ((u64) in[1]) * (in[1] << 1); + tmp[3] = ((u64) in[0]) * (in[3] << 1) + + ((u64) in[1]) * (in[2] << 1); + tmp[4] = ((u64) in[0]) * (in[4] << 1) + + ((u64) in[1]) * (in[3] << 2) + ((u64) in[2]) * in[2]; + tmp[5] = ((u64) in[0]) * (in[5] << 1) + ((u64) in[1]) * + (in[4] << 1) + ((u64) in[2]) * (in[3] << 1); + tmp[6] = ((u64) in[0]) * (in[6] << 1) + ((u64) in[1]) * + (in[5] << 2) + ((u64) in[2]) * (in[4] << 1) + + ((u64) in[3]) * (in[3] << 1); + tmp[7] = ((u64) in[0]) * (in[7] << 1) + ((u64) in[1]) * + (in[6] << 1) + ((u64) in[2]) * (in[5] << 1) + + ((u64) in[3]) * (in[4] << 1); + /* tmp[8] has the greatest value of 2**61 + 2**60 + 2**61 + 2**60 + 2**60, + * which is < 2**64 as required. */ + tmp[8] = ((u64) in[0]) * (in[8] << 1) + ((u64) in[1]) * + (in[7] << 2) + ((u64) in[2]) * (in[6] << 1) + + ((u64) in[3]) * (in[5] << 2) + ((u64) in[4]) * in[4]; + tmp[9] = ((u64) in[1]) * (in[8] << 1) + ((u64) in[2]) * + (in[7] << 1) + ((u64) in[3]) * (in[6] << 1) + + ((u64) in[4]) * (in[5] << 1); + tmp[10] = ((u64) in[2]) * (in[8] << 1) + ((u64) in[3]) * + (in[7] << 2) + ((u64) in[4]) * (in[6] << 1) + + ((u64) in[5]) * (in[5] << 1); + tmp[11] = ((u64) in[3]) * (in[8] << 1) + ((u64) in[4]) * + (in[7] << 1) + ((u64) in[5]) * (in[6] << 1); + tmp[12] = ((u64) in[4]) * (in[8] << 1) + + ((u64) in[5]) * (in[7] << 2) + ((u64) in[6]) * in[6]; + tmp[13] = ((u64) in[5]) * (in[8] << 1) + + ((u64) in[6]) * (in[7] << 1); + tmp[14] = ((u64) in[6]) * (in[8] << 1) + + ((u64) in[7]) * (in[7] << 1); + tmp[15] = ((u64) in[7]) * (in[8] << 1); + tmp[16] = ((u64) in[8]) * in[8]; + + felem_reduce_degree(out, tmp); +} + +/* felem_mul sets out=in*in2. + * + * On entry: in[0,2,...] < 2**30, in[1,3,...] < 2**29 and + * in2[0,2,...] < 2**30, in2[1,3,...] < 2**29. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */ +static void felem_mul(felem out, const felem in, const felem in2) { + u64 tmp[17]; + + tmp[0] = ((u64) in[0]) * in2[0]; + tmp[1] = ((u64) in[0]) * (in2[1] << 0) + + ((u64) in[1]) * (in2[0] << 0); + tmp[2] = ((u64) in[0]) * (in2[2] << 0) + ((u64) in[1]) * + (in2[1] << 1) + ((u64) in[2]) * (in2[0] << 0); + tmp[3] = ((u64) in[0]) * (in2[3] << 0) + ((u64) in[1]) * + (in2[2] << 0) + ((u64) in[2]) * (in2[1] << 0) + + ((u64) in[3]) * (in2[0] << 0); + tmp[4] = ((u64) in[0]) * (in2[4] << 0) + ((u64) in[1]) * + (in2[3] << 1) + ((u64) in[2]) * (in2[2] << 0) + + ((u64) in[3]) * (in2[1] << 1) + + ((u64) in[4]) * (in2[0] << 0); + tmp[5] = ((u64) in[0]) * (in2[5] << 0) + ((u64) in[1]) * + (in2[4] << 0) + ((u64) in[2]) * (in2[3] << 0) + + ((u64) in[3]) * (in2[2] << 0) + ((u64) in[4]) * + (in2[1] << 0) + ((u64) in[5]) * (in2[0] << 0); + tmp[6] = ((u64) in[0]) * (in2[6] << 0) + ((u64) in[1]) * + (in2[5] << 1) + ((u64) in[2]) * (in2[4] << 0) + + ((u64) in[3]) * (in2[3] << 1) + ((u64) in[4]) * + (in2[2] << 0) + ((u64) in[5]) * (in2[1] << 1) + + ((u64) in[6]) * (in2[0] << 0); + tmp[7] = ((u64) in[0]) * (in2[7] << 0) + ((u64) in[1]) * + (in2[6] << 0) + ((u64) in[2]) * (in2[5] << 0) + + ((u64) in[3]) * (in2[4] << 0) + ((u64) in[4]) * + (in2[3] << 0) + ((u64) in[5]) * (in2[2] << 0) + + ((u64) in[6]) * (in2[1] << 0) + + ((u64) in[7]) * (in2[0] << 0); + /* tmp[8] has the greatest value but doesn't overflow. See logic in + * felem_square. */ + tmp[8] = ((u64) in[0]) * (in2[8] << 0) + ((u64) in[1]) * + (in2[7] << 1) + ((u64) in[2]) * (in2[6] << 0) + + ((u64) in[3]) * (in2[5] << 1) + ((u64) in[4]) * + (in2[4] << 0) + ((u64) in[5]) * (in2[3] << 1) + + ((u64) in[6]) * (in2[2] << 0) + ((u64) in[7]) * + (in2[1] << 1) + ((u64) in[8]) * (in2[0] << 0); + tmp[9] = ((u64) in[1]) * (in2[8] << 0) + ((u64) in[2]) * + (in2[7] << 0) + ((u64) in[3]) * (in2[6] << 0) + + ((u64) in[4]) * (in2[5] << 0) + ((u64) in[5]) * + (in2[4] << 0) + ((u64) in[6]) * (in2[3] << 0) + + ((u64) in[7]) * (in2[2] << 0) + + ((u64) in[8]) * (in2[1] << 0); + tmp[10] = ((u64) in[2]) * (in2[8] << 0) + ((u64) in[3]) * + (in2[7] << 1) + ((u64) in[4]) * (in2[6] << 0) + + ((u64) in[5]) * (in2[5] << 1) + ((u64) in[6]) * + (in2[4] << 0) + ((u64) in[7]) * (in2[3] << 1) + + ((u64) in[8]) * (in2[2] << 0); + tmp[11] = ((u64) in[3]) * (in2[8] << 0) + ((u64) in[4]) * + (in2[7] << 0) + ((u64) in[5]) * (in2[6] << 0) + + ((u64) in[6]) * (in2[5] << 0) + ((u64) in[7]) * + (in2[4] << 0) + ((u64) in[8]) * (in2[3] << 0); + tmp[12] = ((u64) in[4]) * (in2[8] << 0) + ((u64) in[5]) * + (in2[7] << 1) + ((u64) in[6]) * (in2[6] << 0) + + ((u64) in[7]) * (in2[5] << 1) + + ((u64) in[8]) * (in2[4] << 0); + tmp[13] = ((u64) in[5]) * (in2[8] << 0) + ((u64) in[6]) * + (in2[7] << 0) + ((u64) in[7]) * (in2[6] << 0) + + ((u64) in[8]) * (in2[5] << 0); + tmp[14] = ((u64) in[6]) * (in2[8] << 0) + ((u64) in[7]) * + (in2[7] << 1) + ((u64) in[8]) * (in2[6] << 0); + tmp[15] = ((u64) in[7]) * (in2[8] << 0) + + ((u64) in[8]) * (in2[7] << 0); + tmp[16] = ((u64) in[8]) * (in2[8] << 0); + + felem_reduce_degree(out, tmp); +} + +static void felem_assign(felem out, const felem in) { + memcpy(out, in, sizeof(felem)); +} + +/* felem_inv calculates |out| = |in|^{-1} + * + * Based on Fermat's Little Theorem: + * a^p = a (mod p) + * a^{p-1} = 1 (mod p) + * a^{p-2} = a^{-1} (mod p) + */ +static void felem_inv(felem out, const felem in) { + felem ftmp, ftmp2; + /* each e_I will hold |in|^{2^I - 1} */ + felem e2, e4, e8, e16, e32, e64; + unsigned i; + + felem_square(ftmp, in); /* 2^1 */ + felem_mul(ftmp, in, ftmp); /* 2^2 - 2^0 */ + felem_assign(e2, ftmp); + felem_square(ftmp, ftmp); /* 2^3 - 2^1 */ + felem_square(ftmp, ftmp); /* 2^4 - 2^2 */ + felem_mul(ftmp, ftmp, e2); /* 2^4 - 2^0 */ + felem_assign(e4, ftmp); + felem_square(ftmp, ftmp); /* 2^5 - 2^1 */ + felem_square(ftmp, ftmp); /* 2^6 - 2^2 */ + felem_square(ftmp, ftmp); /* 2^7 - 2^3 */ + felem_square(ftmp, ftmp); /* 2^8 - 2^4 */ + felem_mul(ftmp, ftmp, e4); /* 2^8 - 2^0 */ + felem_assign(e8, ftmp); + for (i = 0; i < 8; i++) { + felem_square(ftmp, ftmp); + } /* 2^16 - 2^8 */ + felem_mul(ftmp, ftmp, e8); /* 2^16 - 2^0 */ + felem_assign(e16, ftmp); + for (i = 0; i < 16; i++) { + felem_square(ftmp, ftmp); + } /* 2^32 - 2^16 */ + felem_mul(ftmp, ftmp, e16); /* 2^32 - 2^0 */ + felem_assign(e32, ftmp); + for (i = 0; i < 32; i++) { + felem_square(ftmp, ftmp); + } /* 2^64 - 2^32 */ + felem_assign(e64, ftmp); + felem_mul(ftmp, ftmp, in); /* 2^64 - 2^32 + 2^0 */ + for (i = 0; i < 192; i++) { + felem_square(ftmp, ftmp); + } /* 2^256 - 2^224 + 2^192 */ + + felem_mul(ftmp2, e64, e32); /* 2^64 - 2^0 */ + for (i = 0; i < 16; i++) { + felem_square(ftmp2, ftmp2); + } /* 2^80 - 2^16 */ + felem_mul(ftmp2, ftmp2, e16); /* 2^80 - 2^0 */ + for (i = 0; i < 8; i++) { + felem_square(ftmp2, ftmp2); + } /* 2^88 - 2^8 */ + felem_mul(ftmp2, ftmp2, e8); /* 2^88 - 2^0 */ + for (i = 0; i < 4; i++) { + felem_square(ftmp2, ftmp2); + } /* 2^92 - 2^4 */ + felem_mul(ftmp2, ftmp2, e4); /* 2^92 - 2^0 */ + felem_square(ftmp2, ftmp2); /* 2^93 - 2^1 */ + felem_square(ftmp2, ftmp2); /* 2^94 - 2^2 */ + felem_mul(ftmp2, ftmp2, e2); /* 2^94 - 2^0 */ + felem_square(ftmp2, ftmp2); /* 2^95 - 2^1 */ + felem_square(ftmp2, ftmp2); /* 2^96 - 2^2 */ + felem_mul(ftmp2, ftmp2, in); /* 2^96 - 3 */ + + felem_mul(out, ftmp2, ftmp); /* 2^256 - 2^224 + 2^192 + 2^96 - 3 */ +} + +/* felem_scalar_3 sets out=3*out. + * + * On entry: out[0,2,...] < 2**30, out[1,3,...] < 2**29. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */ +static void felem_scalar_3(felem out) { + limb carry = 0; + unsigned i; + + for (i = 0;; i++) { + out[i] *= 3; + out[i] += carry; + carry = out[i] >> 29; + out[i] &= kBottom29Bits; + + i++; + if (i == NLIMBS) + break; + + out[i] *= 3; + out[i] += carry; + carry = out[i] >> 28; + out[i] &= kBottom28Bits; + } + + felem_reduce_carry(out, carry); +} + +/* felem_scalar_4 sets out=4*out. + * + * On entry: out[0,2,...] < 2**30, out[1,3,...] < 2**29. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */ +static void felem_scalar_4(felem out) { + limb carry = 0, next_carry; + unsigned i; + + for (i = 0;; i++) { + next_carry = out[i] >> 27; + out[i] <<= 2; + out[i] &= kBottom29Bits; + out[i] += carry; + carry = next_carry + (out[i] >> 29); + out[i] &= kBottom29Bits; + + i++; + if (i == NLIMBS) + break; + + next_carry = out[i] >> 26; + out[i] <<= 2; + out[i] &= kBottom28Bits; + out[i] += carry; + carry = next_carry + (out[i] >> 28); + out[i] &= kBottom28Bits; + } + + felem_reduce_carry(out, carry); +} + +/* felem_scalar_8 sets out=8*out. + * + * On entry: out[0,2,...] < 2**30, out[1,3,...] < 2**29. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */ +static void felem_scalar_8(felem out) { + limb carry = 0, next_carry; + unsigned i; + + for (i = 0;; i++) { + next_carry = out[i] >> 26; + out[i] <<= 3; + out[i] &= kBottom29Bits; + out[i] += carry; + carry = next_carry + (out[i] >> 29); + out[i] &= kBottom29Bits; + + i++; + if (i == NLIMBS) + break; + + next_carry = out[i] >> 25; + out[i] <<= 3; + out[i] &= kBottom28Bits; + out[i] += carry; + carry = next_carry + (out[i] >> 28); + out[i] &= kBottom28Bits; + } + + felem_reduce_carry(out, carry); +} + +/* felem_is_zero_vartime returns 1 iff |in| == 0. It takes a variable amount of + * time depending on the value of |in|. */ +static char felem_is_zero_vartime(const felem in) { + limb carry; + int i; + limb tmp[NLIMBS]; + + felem_assign(tmp, in); + + /* First, reduce tmp to a minimal form. */ + do { + carry = 0; + for (i = 0;; i++) { + tmp[i] += carry; + carry = tmp[i] >> 29; + tmp[i] &= kBottom29Bits; + + i++; + if (i == NLIMBS) + break; + + tmp[i] += carry; + carry = tmp[i] >> 28; + tmp[i] &= kBottom28Bits; + } + + felem_reduce_carry(tmp, carry); + } while (carry); + + /* tmp < 2**257, so the only possible zero values are 0, p and 2p. */ + return memcmp(tmp, kZero, sizeof(tmp)) == 0 || + memcmp(tmp, kP, sizeof(tmp)) == 0 || + memcmp(tmp, k2P, sizeof(tmp)) == 0; +} + + +/* Group operations: + * + * Elements of the elliptic curve group are represented in Jacobian + * coordinates: (x, y, z). An affine point (x', y') is x'=x/z**2, y'=y/z**3 in + * Jacobian form. */ + +/* point_double sets {x_out,y_out,z_out} = 2*{x,y,z}. + * + * See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l */ +static void point_double(felem x_out, felem y_out, felem z_out, const felem x, + const felem y, const felem z) { + felem delta, gamma, alpha, beta, tmp, tmp2; + + felem_square(delta, z); + felem_square(gamma, y); + felem_mul(beta, x, gamma); + + felem_sum(tmp, x, delta); + felem_diff(tmp2, x, delta); + felem_mul(alpha, tmp, tmp2); + felem_scalar_3(alpha); + + felem_sum(tmp, y, z); + felem_square(tmp, tmp); + felem_diff(tmp, tmp, gamma); + felem_diff(z_out, tmp, delta); + + felem_scalar_4(beta); + felem_square(x_out, alpha); + felem_diff(x_out, x_out, beta); + felem_diff(x_out, x_out, beta); + + felem_diff(tmp, beta, x_out); + felem_mul(tmp, alpha, tmp); + felem_square(tmp2, gamma); + felem_scalar_8(tmp2); + felem_diff(y_out, tmp, tmp2); +} + +/* point_add_mixed sets {x_out,y_out,z_out} = {x1,y1,z1} + {x2,y2,1}. + * (i.e. the second point is affine.) + * + * See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl + * + * Note that this function does not handle P+P, infinity+P nor P+infinity + * correctly. */ +static void point_add_mixed(felem x_out, felem y_out, felem z_out, + const felem x1, const felem y1, const felem z1, + const felem x2, const felem y2) { + felem z1z1, z1z1z1, s2, u2, h, i, j, r, rr, v, tmp; + + felem_square(z1z1, z1); + felem_sum(tmp, z1, z1); + + felem_mul(u2, x2, z1z1); + felem_mul(z1z1z1, z1, z1z1); + felem_mul(s2, y2, z1z1z1); + felem_diff(h, u2, x1); + felem_sum(i, h, h); + felem_square(i, i); + felem_mul(j, h, i); + felem_diff(r, s2, y1); + felem_sum(r, r, r); + felem_mul(v, x1, i); + + felem_mul(z_out, tmp, h); + felem_square(rr, r); + felem_diff(x_out, rr, j); + felem_diff(x_out, x_out, v); + felem_diff(x_out, x_out, v); + + felem_diff(tmp, v, x_out); + felem_mul(y_out, tmp, r); + felem_mul(tmp, y1, j); + felem_diff(y_out, y_out, tmp); + felem_diff(y_out, y_out, tmp); +} + +/* point_add sets {x_out,y_out,z_out} = {x1,y1,z1} + {x2,y2,z2}. + * + * See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl + * + * Note that this function does not handle P+P, infinity+P nor P+infinity + * correctly. */ +static void point_add(felem x_out, felem y_out, felem z_out, const felem x1, + const felem y1, const felem z1, const felem x2, + const felem y2, const felem z2) { + felem z1z1, z1z1z1, z2z2, z2z2z2, s1, s2, u1, u2, h, i, j, r, rr, v, tmp; + + felem_square(z1z1, z1); + felem_square(z2z2, z2); + felem_mul(u1, x1, z2z2); + + felem_sum(tmp, z1, z2); + felem_square(tmp, tmp); + felem_diff(tmp, tmp, z1z1); + felem_diff(tmp, tmp, z2z2); + + felem_mul(z2z2z2, z2, z2z2); + felem_mul(s1, y1, z2z2z2); + + felem_mul(u2, x2, z1z1); + felem_mul(z1z1z1, z1, z1z1); + felem_mul(s2, y2, z1z1z1); + felem_diff(h, u2, u1); + felem_sum(i, h, h); + felem_square(i, i); + felem_mul(j, h, i); + felem_diff(r, s2, s1); + felem_sum(r, r, r); + felem_mul(v, u1, i); + + felem_mul(z_out, tmp, h); + felem_square(rr, r); + felem_diff(x_out, rr, j); + felem_diff(x_out, x_out, v); + felem_diff(x_out, x_out, v); + + felem_diff(tmp, v, x_out); + felem_mul(y_out, tmp, r); + felem_mul(tmp, s1, j); + felem_diff(y_out, y_out, tmp); + felem_diff(y_out, y_out, tmp); +} + +/* point_add_or_double_vartime sets {x_out,y_out,z_out} = {x1,y1,z1} + + * {x2,y2,z2}. + * + * See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl + * + * This function handles the case where {x1,y1,z1}={x2,y2,z2}. */ +static void point_add_or_double_vartime( + felem x_out, felem y_out, felem z_out, const felem x1, const felem y1, + const felem z1, const felem x2, const felem y2, const felem z2) { + felem z1z1, z1z1z1, z2z2, z2z2z2, s1, s2, u1, u2, h, i, j, r, rr, v, tmp; + char x_equal, y_equal; + + felem_square(z1z1, z1); + felem_square(z2z2, z2); + felem_mul(u1, x1, z2z2); + + felem_sum(tmp, z1, z2); + felem_square(tmp, tmp); + felem_diff(tmp, tmp, z1z1); + felem_diff(tmp, tmp, z2z2); + + felem_mul(z2z2z2, z2, z2z2); + felem_mul(s1, y1, z2z2z2); + + felem_mul(u2, x2, z1z1); + felem_mul(z1z1z1, z1, z1z1); + felem_mul(s2, y2, z1z1z1); + felem_diff(h, u2, u1); + x_equal = felem_is_zero_vartime(h); + felem_sum(i, h, h); + felem_square(i, i); + felem_mul(j, h, i); + felem_diff(r, s2, s1); + y_equal = felem_is_zero_vartime(r); + if (x_equal && y_equal) { + point_double(x_out, y_out, z_out, x1, y1, z1); + return; + } + felem_sum(r, r, r); + felem_mul(v, u1, i); + + felem_mul(z_out, tmp, h); + felem_square(rr, r); + felem_diff(x_out, rr, j); + felem_diff(x_out, x_out, v); + felem_diff(x_out, x_out, v); + + felem_diff(tmp, v, x_out); + felem_mul(y_out, tmp, r); + felem_mul(tmp, s1, j); + felem_diff(y_out, y_out, tmp); + felem_diff(y_out, y_out, tmp); +} + +/* copy_conditional sets out=in if mask = 0xffffffff in constant time. + * + * On entry: mask is either 0 or 0xffffffff. */ +static void copy_conditional(felem out, const felem in, limb mask) { + int i; + + for (i = 0; i < NLIMBS; i++) { + const limb tmp = mask & (in[i] ^ out[i]); + out[i] ^= tmp; + } +} + +/* select_affine_point sets {out_x,out_y} to the index'th entry of table. + * On entry: index < 16, table[0] must be zero. */ +static void select_affine_point(felem out_x, felem out_y, const limb* table, + limb index) { + limb i, j; + + memset(out_x, 0, sizeof(felem)); + memset(out_y, 0, sizeof(felem)); + + for (i = 1; i < 16; i++) { + limb mask = i ^ index; + mask |= mask >> 2; + mask |= mask >> 1; + mask &= 1; + mask--; + for (j = 0; j < NLIMBS; j++, table++) { + out_x[j] |= *table & mask; + } + for (j = 0; j < NLIMBS; j++, table++) { + out_y[j] |= *table & mask; + } + } +} + +/* select_jacobian_point sets {out_x,out_y,out_z} to the index'th entry of + * table. On entry: index < 16, table[0] must be zero. */ +static void select_jacobian_point(felem out_x, felem out_y, felem out_z, + const limb* table, limb index) { + limb i, j; + + memset(out_x, 0, sizeof(felem)); + memset(out_y, 0, sizeof(felem)); + memset(out_z, 0, sizeof(felem)); + + /* The implicit value at index 0 is all zero. We don't need to perform that + * iteration of the loop because we already set out_* to zero. */ + table += 3 * NLIMBS; + + // Hit all entries to obscure cache profiling. + for (i = 1; i < 16; i++) { + limb mask = i ^ index; + mask |= mask >> 2; + mask |= mask >> 1; + mask &= 1; + mask--; + for (j = 0; j < NLIMBS; j++, table++) { + out_x[j] |= *table & mask; + } + for (j = 0; j < NLIMBS; j++, table++) { + out_y[j] |= *table & mask; + } + for (j = 0; j < NLIMBS; j++, table++) { + out_z[j] |= *table & mask; + } + } +} + +/* scalar_base_mult sets {nx,ny,nz} = scalar*G where scalar is a little-endian + * number. Note that the value of scalar must be less than the order of the + * group. */ +static void scalar_base_mult(felem nx, felem ny, felem nz, + const p256_int* scalar) { + int i, j; + limb n_is_infinity_mask = -1, p_is_noninfinite_mask, mask; + u32 table_offset; + + felem px, py; + felem tx, ty, tz; + + memset(nx, 0, sizeof(felem)); + memset(ny, 0, sizeof(felem)); + memset(nz, 0, sizeof(felem)); + + /* The loop adds bits at positions 0, 64, 128 and 192, followed by + * positions 32,96,160 and 224 and does this 32 times. */ + for (i = 0; i < 32; i++) { + if (i) { + point_double(nx, ny, nz, nx, ny, nz); + } + table_offset = 0; + for (j = 0; j <= 32; j += 32) { + char bit0 = p256_get_bit(scalar, 31 - i + j); + char bit1 = p256_get_bit(scalar, 95 - i + j); + char bit2 = p256_get_bit(scalar, 159 - i + j); + char bit3 = p256_get_bit(scalar, 223 - i + j); + limb index = bit0 | (bit1 << 1) | (bit2 << 2) | (bit3 << 3); + + select_affine_point(px, py, kPrecomputed + table_offset, index); + table_offset += 30 * NLIMBS; + + /* Since scalar is less than the order of the group, we know that + * {nx,ny,nz} != {px,py,1}, unless both are zero, which we handle + * below. */ + point_add_mixed(tx, ty, tz, nx, ny, nz, px, py); + /* The result of point_add_mixed is incorrect if {nx,ny,nz} is zero + * (a.k.a. the point at infinity). We handle that situation by + * copying the point from the table. */ + copy_conditional(nx, px, n_is_infinity_mask); + copy_conditional(ny, py, n_is_infinity_mask); + copy_conditional(nz, kOne, n_is_infinity_mask); + + /* Equally, the result is also wrong if the point from the table is + * zero, which happens when the index is zero. We handle that by + * only copying from {tx,ty,tz} to {nx,ny,nz} if index != 0. */ + p_is_noninfinite_mask = NON_ZERO_TO_ALL_ONES(index); + mask = p_is_noninfinite_mask & ~n_is_infinity_mask; + copy_conditional(nx, tx, mask); + copy_conditional(ny, ty, mask); + copy_conditional(nz, tz, mask); + /* If p was not zero, then n is now non-zero. */ + n_is_infinity_mask &= ~p_is_noninfinite_mask; + } + } +} + +/* point_to_affine converts a Jacobian point to an affine point. If the input + * is the point at infinity then it returns (0, 0) in constant time. */ +static void point_to_affine(felem x_out, felem y_out, const felem nx, + const felem ny, const felem nz) { + felem z_inv, z_inv_sq; + felem_inv(z_inv, nz); + felem_square(z_inv_sq, z_inv); + felem_mul(x_out, nx, z_inv_sq); + felem_mul(z_inv, z_inv, z_inv_sq); + felem_mul(y_out, ny, z_inv); +} + +/* scalar_base_mult sets {nx,ny,nz} = scalar*{x,y}. */ +static void scalar_mult(felem nx, felem ny, felem nz, const felem x, + const felem y, const p256_int* scalar) { + int i; + felem px, py, pz, tx, ty, tz; + felem precomp[16][3]; + limb n_is_infinity_mask, index, p_is_noninfinite_mask, mask; + + /* We precompute 0,1,2,... times {x,y}. */ + memset(precomp, 0, sizeof(felem) * 3); + memcpy(&precomp[1][0], x, sizeof(felem)); + memcpy(&precomp[1][1], y, sizeof(felem)); + memcpy(&precomp[1][2], kOne, sizeof(felem)); + + for (i = 2; i < 16; i += 2) { + point_double(precomp[i][0], precomp[i][1], precomp[i][2], + precomp[i / 2][0], precomp[i / 2][1], precomp[i / 2][2]); + + point_add_mixed(precomp[i + 1][0], precomp[i + 1][1], precomp[i + 1][2], + precomp[i][0], precomp[i][1], precomp[i][2], x, y); + } + + memset(nx, 0, sizeof(felem)); + memset(ny, 0, sizeof(felem)); + memset(nz, 0, sizeof(felem)); + n_is_infinity_mask = -1; + + /* We add in a window of four bits each iteration and do this 64 times. */ + for (i = 0; i < 256; i += 4) { + if (i) { + point_double(nx, ny, nz, nx, ny, nz); + point_double(nx, ny, nz, nx, ny, nz); + point_double(nx, ny, nz, nx, ny, nz); + point_double(nx, ny, nz, nx, ny, nz); + } + + index = (p256_get_bit(scalar, 255 - i - 0) << 3) | + (p256_get_bit(scalar, 255 - i - 1) << 2) | + (p256_get_bit(scalar, 255 - i - 2) << 1) | + p256_get_bit(scalar, 255 - i - 3); + + /* See the comments in scalar_base_mult about handling infinities. */ + select_jacobian_point(px, py, pz, precomp[0][0], index); + point_add(tx, ty, tz, nx, ny, nz, px, py, pz); + copy_conditional(nx, px, n_is_infinity_mask); + copy_conditional(ny, py, n_is_infinity_mask); + copy_conditional(nz, pz, n_is_infinity_mask); + + p_is_noninfinite_mask = NON_ZERO_TO_ALL_ONES(index); + mask = p_is_noninfinite_mask & ~n_is_infinity_mask; + + copy_conditional(nx, tx, mask); + copy_conditional(ny, ty, mask); + copy_conditional(nz, tz, mask); + n_is_infinity_mask &= ~p_is_noninfinite_mask; + } +} + +#define kRDigits {2, 0, 0, 0xfffffffe, 0xffffffff, 0xffffffff, 0xfffffffd, 1} // 2^257 mod p256.p + +#define kRInvDigits {0x80000000, 1, 0xffffffff, 0, 0x80000001, 0xfffffffe, 1, 0x7fffffff} // 1 / 2^257 mod p256.p + +static const p256_int kR = { kRDigits }; +static const p256_int kRInv = { kRInvDigits }; + +/* to_montgomery sets out = R*in. */ +static void to_montgomery(felem out, const p256_int* in) { + p256_int in_shifted; + int i; + + p256_init(&in_shifted); + p256_modmul(&SECP256r1_p, in, 0, &kR, &in_shifted); + + for (i = 0; i < NLIMBS; i++) { + if ((i & 1) == 0) { + out[i] = P256_DIGIT(&in_shifted, 0) & kBottom29Bits; + p256_shr(&in_shifted, 29, &in_shifted); + } else { + out[i] = P256_DIGIT(&in_shifted, 0) & kBottom28Bits; + p256_shr(&in_shifted, 28, &in_shifted); + } + } + + p256_clear(&in_shifted); +} + +/* from_montgomery sets out=in/R. */ +static void from_montgomery(p256_int* out, const felem in) { + p256_int result, tmp; + int i, top; + + p256_init(&result); + p256_init(&tmp); + + p256_add_d(&tmp, in[NLIMBS - 1], &result); + for (i = NLIMBS - 2; i >= 0; i--) { + if ((i & 1) == 0) { + top = p256_shl(&result, 29, &tmp); + } else { + top = p256_shl(&result, 28, &tmp); + } + top |= p256_add_d(&tmp, in[i], &result); + } + + p256_modmul(&SECP256r1_p, &kRInv, top, &result, out); + + p256_clear(&result); + p256_clear(&tmp); +} + +/* p256_base_point_mul sets {out_x,out_y} = nG, where n is < the + * order of the group. */ +void p256_base_point_mul(const p256_int* n, p256_int* out_x, p256_int* out_y) { + felem x, y, z; + + scalar_base_mult(x, y, z, n); + + { + felem x_affine, y_affine; + + point_to_affine(x_affine, y_affine, x, y, z); + from_montgomery(out_x, x_affine); + from_montgomery(out_y, y_affine); + } +} + +/* p256_points_mul_vartime sets {out_x,out_y} = n1*G + n2*{in_x,in_y}, where + * n1 and n2 are < the order of the group. + * + * As indicated by the name, this function operates in variable time. This + * is safe because it's used for signature validation which doesn't deal + * with secrets. */ +void p256_points_mul_vartime( + const p256_int* n1, const p256_int* n2, const p256_int* in_x, + const p256_int* in_y, p256_int* out_x, p256_int* out_y) { + felem x1, y1, z1, x2, y2, z2, px, py; + + /* If both scalars are zero, then the result is the point at infinity. */ + if (p256_is_zero(n1) != 0 && p256_is_zero(n2) != 0) { + p256_clear(out_x); + p256_clear(out_y); + return; + } + + to_montgomery(px, in_x); + to_montgomery(py, in_y); + scalar_base_mult(x1, y1, z1, n1); + scalar_mult(x2, y2, z2, px, py, n2); + + if (p256_is_zero(n2) != 0) { + /* If n2 == 0, then {x2,y2,z2} is zero and the result is just + * {x1,y1,z1}. */ + } else if (p256_is_zero(n1) != 0) { + /* If n1 == 0, then {x1,y1,z1} is zero and the result is just + * {x2,y2,z2}. */ + memcpy(x1, x2, sizeof(x2)); + memcpy(y1, y2, sizeof(y2)); + memcpy(z1, z2, sizeof(z2)); + } else { + /* This function handles the case where {x1,y1,z1} == {x2,y2,z2}. */ + point_add_or_double_vartime(x1, y1, z1, x1, y1, z1, x2, y2, z2); + } + + point_to_affine(px, py, x1, y1, z1); + from_montgomery(out_x, px); + from_montgomery(out_y, py); +} diff --git a/libmincrypt/p256_ecdsa.c b/libmincrypt/p256_ecdsa.c new file mode 100644 index 0000000..f2264b0 --- /dev/null +++ b/libmincrypt/p256_ecdsa.c @@ -0,0 +1,56 @@ +/* + * Copyright 2013 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 <string.h> + +#include "mincrypt/p256_ecdsa.h" +#include "mincrypt/p256.h" + +int p256_ecdsa_verify(const p256_int* key_x, const p256_int* key_y, + const p256_int* message, + const p256_int* r, const p256_int* s) { + p256_int u, v; + + // Check public key. + if (!p256_is_valid_point(key_x, key_y)) return 0; + + // Check r and s are != 0 % n. + p256_mod(&SECP256r1_n, r, &u); + p256_mod(&SECP256r1_n, s, &v); + if (p256_is_zero(&u) || p256_is_zero(&v)) return 0; + + p256_modinv_vartime(&SECP256r1_n, s, &v); + p256_modmul(&SECP256r1_n, message, 0, &v, &u); // message / s % n + p256_modmul(&SECP256r1_n, r, 0, &v, &v); // r / s % n + + p256_points_mul_vartime(&u, &v, + key_x, key_y, + &u, &v); + + p256_mod(&SECP256r1_n, &u, &u); // (x coord % p) % n + return p256_cmp(r, &u) == 0; +} + diff --git a/libmincrypt/test/Android.mk b/libmincrypt/test/Android.mk index a28ccd8..73ff7d0 100644 --- a/libmincrypt/test/Android.mk +++ b/libmincrypt/test/Android.mk @@ -1,10 +1,15 @@ # Copyright 2013 The Android Open Source Project LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) +include $(CLEAR_VARS) LOCAL_MODULE := rsa_test LOCAL_SRC_FILES := rsa_test.c LOCAL_STATIC_LIBRARIES := libmincrypt -include $(BUILD_HOST_EXECUTABLE) +include $(BUILD_HOST_NATIVE_TEST) +include $(CLEAR_VARS) +LOCAL_MODULE := ecdsa_test +LOCAL_SRC_FILES := ecdsa_test.c +LOCAL_STATIC_LIBRARIES := libmincrypt +include $(BUILD_HOST_NATIVE_TEST) diff --git a/libmincrypt/test/ecdsa_test.c b/libmincrypt/test/ecdsa_test.c new file mode 100644 index 0000000..b5a7b3a --- /dev/null +++ b/libmincrypt/test/ecdsa_test.c @@ -0,0 +1,278 @@ +/* + * Copyright 2013 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 <stdio.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +#include "mincrypt/p256.h" +#include "mincrypt/p256_ecdsa.h" +#include "mincrypt/sha256.h" + +/** + * Messages signed using: + * +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIDw6UiziVMbjlfSpOAIpA2tcL+v1OlznZLnpadO8BGi1oAoGCCqGSM49 +AwEHoUQDQgAEZw7VAOjAXYRFuhZWYBgjahdOvkwcAnjGkxQWytZW+iS1hI3ZGE24 +6XmNka9IGxAgj2n/ip+MuZJMFoJ9DRea3g== +-----END EC PRIVATE KEY----- + */ + +p256_int key_x = { + .a = {0xd656fa24u, 0x931416cau, 0x1c0278c6u, 0x174ebe4cu, + 0x6018236au, 0x45ba1656u, 0xe8c05d84u, 0x670ed500u} +}; +p256_int key_y = { + .a = {0x0d179adeu, 0x4c16827du, 0x9f8cb992u, 0x8f69ff8au, + 0x481b1020u, 0x798d91afu, 0x184db8e9u, 0xb5848dd9u} +}; + +char* message_1 = + "f4 5d 55 f3 55 51 e9 75 d6 a8 dc 7e a9 f4 88 59" + "39 40 cc 75 69 4a 27 8f 27 e5 78 a1 63 d8 39 b3" + "40 40 84 18 08 cf 9c 58 c9 b8 72 8b f5 f9 ce 8e" + "e8 11 ea 91 71 4f 47 ba b9 2d 0f 6d 5a 26 fc fe" + "ea 6c d9 3b 91 0c 0a 2c 96 3e 64 eb 18 23 f1 02" + "75 3d 41 f0 33 59 10 ad 3a 97 71 04 f1 aa f6 c3" + "74 27 16 a9 75 5d 11 b8 ee d6 90 47 7f 44 5c 5d" + "27 20 8b 2e 28 43 30 fa 3d 30 14 23 fa 7f 2d 08" + "6e 0a d0 b8 92 b9 db 54 4e 45 6d 3f 0d ab 85 d9" + "53 c1 2d 34 0a a8 73 ed a7 27 c8 a6 49 db 7f a6" + "37 40 e2 5e 9a f1 53 3b 30 7e 61 32 99 93 11 0e" + "95 19 4e 03 93 99 c3 82 4d 24 c5 1f 22 b2 6b de" + "10 24 cd 39 59 58 a2 df eb 48 16 a6 e8 ad ed b5" + "0b 1f 6b 56 d0 b3 06 0f f0 f1 c4 cb 0d 0e 00 1d" + "d5 9d 73 be 12"; + +char* signature_1 = + "30 44 02 20 43 18 fc eb 3b a8 3a a8 a3 cf 41 b7" + "81 4a f9 01 e1 8b 6e 95 c1 3a 83 25 9e a5 2e 66" + "7c 98 25 d9 02 20 54 f3 7f 5a e9 36 9c a2 f0 51" + "e0 6e 78 48 60 a3 f9 8a d5 2c 37 5a 0a 29 c9 f7" + "ea 57 7e 88 46 12"; + +// Same as signature 1, but with leading zeroes. +char* message_2 = + "f4 5d 55 f3 55 51 e9 75 d6 a8 dc 7e a9 f4 88 59" + "39 40 cc 75 69 4a 27 8f 27 e5 78 a1 63 d8 39 b3" + "40 40 84 18 08 cf 9c 58 c9 b8 72 8b f5 f9 ce 8e" + "e8 11 ea 91 71 4f 47 ba b9 2d 0f 6d 5a 26 fc fe" + "ea 6c d9 3b 91 0c 0a 2c 96 3e 64 eb 18 23 f1 02" + "75 3d 41 f0 33 59 10 ad 3a 97 71 04 f1 aa f6 c3" + "74 27 16 a9 75 5d 11 b8 ee d6 90 47 7f 44 5c 5d" + "27 20 8b 2e 28 43 30 fa 3d 30 14 23 fa 7f 2d 08" + "6e 0a d0 b8 92 b9 db 54 4e 45 6d 3f 0d ab 85 d9" + "53 c1 2d 34 0a a8 73 ed a7 27 c8 a6 49 db 7f a6" + "37 40 e2 5e 9a f1 53 3b 30 7e 61 32 99 93 11 0e" + "95 19 4e 03 93 99 c3 82 4d 24 c5 1f 22 b2 6b de" + "10 24 cd 39 59 58 a2 df eb 48 16 a6 e8 ad ed b5" + "0b 1f 6b 56 d0 b3 06 0f f0 f1 c4 cb 0d 0e 00 1d" + "d5 9d 73 be 12"; + +char* signature_2 = + "30 46 02 21 00 43 18 fc eb 3b a8 3a a8 a3 cf 41 b7" + "81 4a f9 01 e1 8b 6e 95 c1 3a 83 25 9e a5 2e 66" + "7c 98 25 d9 02 21 00 54 f3 7f 5a e9 36 9c a2 f0 51" + "e0 6e 78 48 60 a3 f9 8a d5 2c 37 5a 0a 29 c9 f7" + "ea 57 7e 88 46 12"; + +// Excessive zeroes on the signature +char* message_3 = + "f4 5d 55 f3 55 51 e9 75 d6 a8 dc 7e a9 f4 88 59" + "39 40 cc 75 69 4a 27 8f 27 e5 78 a1 63 d8 39 b3" + "40 40 84 18 08 cf 9c 58 c9 b8 72 8b f5 f9 ce 8e" + "e8 11 ea 91 71 4f 47 ba b9 2d 0f 6d 5a 26 fc fe" + "ea 6c d9 3b 91 0c 0a 2c 96 3e 64 eb 18 23 f1 02" + "75 3d 41 f0 33 59 10 ad 3a 97 71 04 f1 aa f6 c3" + "74 27 16 a9 75 5d 11 b8 ee d6 90 47 7f 44 5c 5d" + "27 20 8b 2e 28 43 30 fa 3d 30 14 23 fa 7f 2d 08" + "6e 0a d0 b8 92 b9 db 54 4e 45 6d 3f 0d ab 85 d9" + "53 c1 2d 34 0a a8 73 ed a7 27 c8 a6 49 db 7f a6" + "37 40 e2 5e 9a f1 53 3b 30 7e 61 32 99 93 11 0e" + "95 19 4e 03 93 99 c3 82 4d 24 c5 1f 22 b2 6b de" + "10 24 cd 39 59 58 a2 df eb 48 16 a6 e8 ad ed b5" + "0b 1f 6b 56 d0 b3 06 0f f0 f1 c4 cb 0d 0e 00 1d" + "d5 9d 73 be 12"; + +char* signature_3 = + "30 4c 02 24 00 00 00 00 43 18 fc eb 3b a8 3a a8 a3 cf 41 b7" + "81 4a f9 01 e1 8b 6e 95 c1 3a 83 25 9e a5 2e 66" + "7c 98 25 d9 02 24 00 00 00 00 54 f3 7f 5a e9 36 9c a2 f0 51" + "e0 6e 78 48 60 a3 f9 8a d5 2c 37 5a 0a 29 c9 f7" + "ea 57 7e 88 46 12"; + + +char* good_dsa_signature_1 = + "30 0D 02 01 01 02 08 00 A5 55 5A 01 FF A5 01"; +p256_int good_dsa_signature_1_r = { + .a = {0x00000001U, 0x00000000U, 0x00000000U, 0x00000000U, + 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U} +}; +p256_int good_dsa_signature_1_s = { + .a = {0x01FFA501U, 0x00A5555AU, 0x00000000U, 0x00000000U, + 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U} +}; + + +char* bad_dsa_signature_1 = + "a0 06 02 01 01 02 01 01"; + +char* bad_dsa_signature_2 = + "30 07 02 01 01 02 01 01"; + +char* bad_dsa_signature_3 = + "30 06 82 01 01 02 01 01"; + +char* bad_dsa_signature_4 = + "30 06 02 00 01 02 01 01"; + +char* bad_dsa_signature_5 = + "30 06 02 01 01 82 01 01"; + +char* bad_dsa_signature_6 = + "30 05 02 01 01 02 00"; + +char* bad_dsa_signature_7 = + "30 06 02 01 01 02 00 01"; + +unsigned char* parsehex(char* str, int* len) { + // result can't be longer than input + unsigned char* result = malloc(strlen(str)); + + unsigned char* p = result; + *len = 0; + + while (*str) { + int b; + + while (isspace(*str)) str++; + + switch (*str) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + b = (*str - '0') << 4; break; + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + b = (*str - 'a' + 10) << 4; break; + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + b = (*str - 'A' + 10) << 4; break; + case '\0': + return result; + default: + return NULL; + } + str++; + + while (isspace(*str)) str++; + + switch (*str) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + b |= *str - '0'; break; + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + b |= *str - 'a' + 10; break; + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + b |= *str - 'A' + 10; break; + default: + return NULL; + } + str++; + + *p++ = b; + ++*len; + } + + return result; +} + +int main(int arg, char** argv) { + + unsigned char hash_buf[SHA256_DIGEST_SIZE]; + + unsigned char* message; + int mlen; + unsigned char* signature; + int slen; + + p256_int hash; + p256_int r; + p256_int s; + + int success = 1; + +#define CHECK_DSA_SIG(sig, good) do {\ + message = parsehex(sig, &mlen); \ + int result = dsa_sig_unpack(message, mlen, &r, &s); \ + printf(#sig ": %s\n", result ? "good" : "bad"); \ + success = success && !(good ^ result); \ + free(message); \ + } while(0) +#define CHECK_GOOD_DSA_SIG(n) do {\ + CHECK_DSA_SIG(good_dsa_signature_##n, 1); \ + int result = !memcmp(P256_DIGITS(&good_dsa_signature_##n##_r), P256_DIGITS(&r), \ + P256_NBYTES); \ + success = success && result; \ + printf(" R value %s\n", result ? "good" : "bad"); \ + result = !memcmp(P256_DIGITS(&good_dsa_signature_##n##_s), P256_DIGITS(&s), \ + P256_NBYTES); \ + success = success && result; \ + printf(" S value %s\n", result ? "good" : "bad"); \ + } while (0) +#define CHECK_BAD_DSA_SIG(n) \ + CHECK_DSA_SIG(bad_dsa_signature_##n, 0) + + CHECK_GOOD_DSA_SIG(1); + + CHECK_BAD_DSA_SIG(1); + CHECK_BAD_DSA_SIG(2); + CHECK_BAD_DSA_SIG(3); + CHECK_BAD_DSA_SIG(4); + CHECK_BAD_DSA_SIG(5); + CHECK_BAD_DSA_SIG(6); + CHECK_BAD_DSA_SIG(7); + + +#define TEST_MESSAGE(n) do {\ + message = parsehex(message_##n, &mlen); \ + SHA256_hash(message, mlen, hash_buf); \ + p256_from_bin(hash_buf, &hash); \ + signature = parsehex(signature_##n, &slen); \ + int result = dsa_sig_unpack(signature, slen, &r, &s); \ + if (result) { result = p256_ecdsa_verify(&key_x, &key_y, &hash, &r, &s); } \ + printf("message %d: %s\n", n, result ? "verified" : "not verified"); \ + success = success && result; \ + free(signature); \ + } while(0) + + TEST_MESSAGE(1); + TEST_MESSAGE(2); + TEST_MESSAGE(3); + + printf("\n%s\n\n", success ? "PASS" : "FAIL"); + + return !success; +} diff --git a/libmincrypt/tools/Android.mk b/libmincrypt/tools/Android.mk index b61234a..3154914 100644 --- a/libmincrypt/tools/Android.mk +++ b/libmincrypt/tools/Android.mk @@ -13,9 +13,10 @@ # limitations under the License. LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) +include $(CLEAR_VARS) LOCAL_MODULE := dumpkey LOCAL_SRC_FILES := DumpPublicKey.java LOCAL_JAR_MANIFEST := DumpPublicKey.mf +LOCAL_STATIC_JAVA_LIBRARIES := bouncycastle-host include $(BUILD_HOST_JAVA_LIBRARY) diff --git a/libmincrypt/tools/DumpPublicKey.java b/libmincrypt/tools/DumpPublicKey.java index 7189116..3eb1398 100644 --- a/libmincrypt/tools/DumpPublicKey.java +++ b/libmincrypt/tools/DumpPublicKey.java @@ -16,6 +16,8 @@ package com.android.dumpkey; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + import java.io.FileInputStream; import java.math.BigInteger; import java.security.cert.CertificateFactory; @@ -23,7 +25,10 @@ import java.security.cert.X509Certificate; import java.security.KeyStore; import java.security.Key; import java.security.PublicKey; +import java.security.Security; +import java.security.interfaces.ECPublicKey; import java.security.interfaces.RSAPublicKey; +import java.security.spec.ECPoint; /** * Command line tool to extract RSA public keys from X.509 certificates @@ -39,9 +44,8 @@ class DumpPublicKey { * 3: 2048-bit RSA key with e=3 and SHA-256 hash * 4: 2048-bit RSA key with e=65537 and SHA-256 hash * @throws Exception if the key has the wrong size or public exponent - */ - static int check(RSAPublicKey key, boolean useSHA256) throws Exception { + static int checkRSA(RSAPublicKey key, boolean useSHA256) throws Exception { BigInteger pubexp = key.getPublicExponent(); BigInteger modulus = key.getModulus(); int version; @@ -64,12 +68,42 @@ class DumpPublicKey { } /** + * @param key to perform sanity checks on + * @return version number of key. Supported versions are: + * 5: 256-bit EC key with curve NIST P-256 + * @throws Exception if the key has the wrong size or public exponent + */ + static int checkEC(ECPublicKey key) throws Exception { + if (key.getParams().getCurve().getField().getFieldSize() != 256) { + throw new Exception("Curve must be NIST P-256"); + } + + return 5; + } + + /** + * Perform sanity check on public key. + */ + static int check(PublicKey key, boolean useSHA256) throws Exception { + if (key instanceof RSAPublicKey) { + return checkRSA((RSAPublicKey) key, useSHA256); + } else if (key instanceof ECPublicKey) { + if (!useSHA256) { + throw new Exception("Must use SHA-256 with EC keys!"); + } + return checkEC((ECPublicKey) key); + } else { + throw new Exception("Unsupported key class: " + key.getClass().getName()); + } + } + + /** * @param key to output * @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, boolean useSHA256) throws Exception { + static String printRSA(RSAPublicKey key, boolean useSHA256) throws Exception { int version = check(key, useSHA256); BigInteger N = key.getModulus(); @@ -128,11 +162,78 @@ class DumpPublicKey { return result.toString(); } + /** + * @param key to output + * @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 printEC(ECPublicKey key) throws Exception { + int version = checkEC(key); + + StringBuilder result = new StringBuilder(); + + result.append("v"); + result.append(Integer.toString(version)); + result.append(" "); + + BigInteger X = key.getW().getAffineX(); + BigInteger Y = key.getW().getAffineY(); + int nbytes = key.getParams().getCurve().getField().getFieldSize() / 8; // # of 32 bit integers in X coordinate + + result.append("{"); + result.append(nbytes); + + BigInteger B = BigInteger.valueOf(0x100L); // 2^8 + + // Write out Y coordinate as array of characters. + result.append(",{"); + for (int i = 0; i < nbytes; ++i) { + long n = X.mod(B).longValue(); + result.append(n); + + if (i != nbytes - 1) { + result.append(","); + } + + X = X.divide(B); + } + result.append("}"); + + // Write out Y coordinate as array of characters. + result.append(",{"); + for (int i = 0; i < nbytes; ++i) { + long n = Y.mod(B).longValue(); + result.append(n); + + if (i != nbytes - 1) { + result.append(","); + } + + Y = Y.divide(B); + } + result.append("}"); + + result.append("}"); + return result.toString(); + } + + static String print(PublicKey key, boolean useSHA256) throws Exception { + if (key instanceof RSAPublicKey) { + return printRSA((RSAPublicKey) key, useSHA256); + } else if (key instanceof ECPublicKey) { + return printEC((ECPublicKey) key); + } else { + throw new Exception("Unsupported key class: " + key.getClass().getName()); + } + } + public static void main(String[] args) { if (args.length < 1) { System.err.println("Usage: DumpPublicKey certfile ... > source.c"); System.exit(1); } + Security.addProvider(new BouncyCastleProvider()); try { for (int i = 0; i < args.length; i++) { FileInputStream input = new FileInputStream(args[i]); @@ -147,7 +248,7 @@ class DumpPublicKey { // anyway. Continue to do so for backwards // compatibility. useSHA256 = false; - } else if ("SHA256withRSA".equals(sigAlg)) { + } else if ("SHA256withRSA".equals(sigAlg) || "SHA256withECDSA".equals(sigAlg)) { useSHA256 = true; } else { System.err.println(args[i] + ": unsupported signature algorithm \"" + @@ -155,7 +256,7 @@ class DumpPublicKey { System.exit(1); } - RSAPublicKey key = (RSAPublicKey) (cert.getPublicKey()); + PublicKey key = cert.getPublicKey(); check(key, useSHA256); System.out.print(print(key, useSHA256)); System.out.println(i < args.length - 1 ? "," : ""); diff --git a/libnetutils/ifc_utils.c b/libnetutils/ifc_utils.c index eb33d06..4d004f6 100644 --- a/libnetutils/ifc_utils.c +++ b/libnetutils/ifc_utils.c @@ -50,6 +50,11 @@ #define ALOGW printf #endif +#ifdef HAVE_ANDROID_OS +/* SIOCKILLADDR is an Android extension. */ +#define SIOCKILLADDR 0x8939 +#endif + static int ifc_ctl_sock = -1; static int ifc_ctl_sock6 = -1; void printerr(char *fmt, ...); diff --git a/libpixelflinger/Android.mk b/libpixelflinger/Android.mk index a9057d7..fb8478c 100644 --- a/libpixelflinger/Android.mk +++ b/libpixelflinger/Android.mk @@ -9,13 +9,11 @@ include $(CLEAR_VARS) PIXELFLINGER_SRC_FILES:= \ codeflinger/ARMAssemblerInterface.cpp \ codeflinger/ARMAssemblerProxy.cpp \ - codeflinger/ARMAssembler.cpp \ codeflinger/CodeCache.cpp \ codeflinger/GGLAssembler.cpp \ codeflinger/load_store.cpp \ codeflinger/blending.cpp \ codeflinger/texturing.cpp \ - codeflinger/disassem.c \ fixed.cpp.arm \ picker.cpp.arm \ pixelflinger.cpp.arm \ @@ -37,6 +35,8 @@ endif endif ifeq ($(TARGET_ARCH),arm) +PIXELFLINGER_SRC_FILES += codeflinger/ARMAssembler.cpp +PIXELFLINGER_SRC_FILES += codeflinger/disassem.c # special optimization flags for pixelflinger PIXELFLINGER_CFLAGS += -fstrict-aliasing -fomit-frame-pointer endif @@ -50,6 +50,14 @@ endif LOCAL_SHARED_LIBRARIES := libcutils liblog +ifeq ($(TARGET_ARCH),arm64) +PIXELFLINGER_SRC_FILES += arch-arm64/t32cb16blend.S +PIXELFLINGER_SRC_FILES += arch-arm64/col32cb16blend.S +PIXELFLINGER_SRC_FILES += codeflinger/Arm64Assembler.cpp +PIXELFLINGER_SRC_FILES += codeflinger/Arm64Disassembler.cpp +PIXELFLINGER_CFLAGS += -fstrict-aliasing -fomit-frame-pointer +endif + # # Shared library # diff --git a/libpixelflinger/arch-arm64/col32cb16blend.S b/libpixelflinger/arch-arm64/col32cb16blend.S new file mode 100644 index 0000000..18a01fd --- /dev/null +++ b/libpixelflinger/arch-arm64/col32cb16blend.S @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + .text + .align + + .global scanline_col32cb16blend_arm64 + +// +// This function alpha blends a fixed color into a destination scanline, using +// the formula: +// +// d = s + (((a + (a >> 7)) * d) >> 8) +// +// where d is the destination pixel, +// s is the source color, +// a is the alpha channel of the source color. +// + +// x0 = destination buffer pointer +// w1 = color value +// w2 = count + + +scanline_col32cb16blend_arm64: + + lsr w5, w1, #24 // shift down alpha + mov w9, #0xff // create mask + add w5, w5, w5, lsr #7 // add in top bit + mov w4, #256 // create #0x100 + sub w5, w4, w5 // invert alpha + and w10, w1, #0xff // extract red + and w12, w9, w1, lsr #8 // extract green + and w4, w9, w1, lsr #16 // extract blue + lsl w10, w10, #5 // prescale red + lsl w12, w12, #6 // prescale green + lsl w4, w4, #5 // prescale blue + lsr w9, w9, #2 // create dest green mask + +1: + ldrh w8, [x0] // load dest pixel + subs w2, w2, #1 // decrement loop counter + lsr w6, w8, #11 // extract dest red + and w7, w9, w8, lsr #5 // extract dest green + and w8, w8, #0x1f // extract dest blue + + madd w6, w6, w5, w10 // dest red * alpha + src red + madd w7, w7, w5, w12 // dest green * alpha + src green + madd w8, w8, w5, w4 // dest blue * alpha + src blue + + lsr w6, w6, #8 // shift down red + lsr w7, w7, #8 // shift down green + lsl w6, w6, #11 // shift red into 565 + orr w6, w6, w7, lsl #5 // shift green into 565 + orr w6, w6, w8, lsr #8 // shift blue into 565 + + strh w6, [x0], #2 // store pixel to dest, update ptr + b.ne 1b // if count != 0, loop + + ret + + + diff --git a/libpixelflinger/arch-arm64/t32cb16blend.S b/libpixelflinger/arch-arm64/t32cb16blend.S new file mode 100644 index 0000000..7da8cf5 --- /dev/null +++ b/libpixelflinger/arch-arm64/t32cb16blend.S @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + .text + .align + + .global scanline_t32cb16blend_arm64 + +/* + * .macro pixel + * + * This macro alpha blends RGB565 original pixel located in either + * top or bottom 16 bits of DREG register with SRC 32 bit pixel value + * and writes the result to FB register + * + * \DREG is a 32-bit register containing *two* original destination RGB565 + * pixels, with the even one in the low-16 bits, and the odd one in the + * high 16 bits. + * + * \SRC is a 32-bit 0xAABBGGRR pixel value, with pre-multiplied colors. + * + * \FB is a target register that will contain the blended pixel values. + * + * \ODD is either 0 or 1 and indicates if we're blending the lower or + * upper 16-bit pixels in DREG into FB + * + * + * clobbered: w6, w7, w16, w17, w18 + * + */ + +.macro pixel, DREG, SRC, FB, ODD + + // SRC = 0xAABBGGRR + lsr w7, \SRC, #24 // sA + add w7, w7, w7, lsr #7 // sA + (sA >> 7) + mov w6, #0x100 + sub w7, w6, w7 // sA = 0x100 - (sA+(sA>>7)) + +1: + +.if \ODD //Blending odd pixel present in top 16 bits of DREG register + + // red + lsr w16, \DREG, #(16 + 11) + mul w16, w7, w16 + lsr w6, \SRC, #3 + and w6, w6, #0x1F + add w16, w6, w16, lsr #8 + cmp w16, #0x1F + orr w17, \FB, #(0x1F<<(16 + 11)) + orr w18, \FB, w16, lsl #(16 + 11) + csel \FB, w17, w18, hi + // green + and w6, \DREG, #(0x3F<<(16 + 5)) + lsr w17,w6,#(16+5) + mul w6, w7, w17 + lsr w16, \SRC, #(8+2) + and w16, w16, #0x3F + add w6, w16, w6, lsr #8 + cmp w6, #0x3F + orr w17, \FB, #(0x3F<<(16 + 5)) + orr w18, \FB, w6, lsl #(16 + 5) + csel \FB, w17, w18, hi + // blue + and w16, \DREG, #(0x1F << 16) + lsr w17,w16,#16 + mul w16, w7, w17 + lsr w6, \SRC, #(8+8+3) + and w6, w6, #0x1F + add w16, w6, w16, lsr #8 + cmp w16, #0x1F + orr w17, \FB, #(0x1F << 16) + orr w18, \FB, w16, lsl #16 + csel \FB, w17, w18, hi + +.else //Blending even pixel present in bottom 16 bits of DREG register + + // red + lsr w16, \DREG, #11 + and w16, w16, #0x1F + mul w16, w7, w16 + lsr w6, \SRC, #3 + and w6, w6, #0x1F + add w16, w6, w16, lsr #8 + cmp w16, #0x1F + mov w17, #(0x1F<<11) + lsl w18, w16, #11 + csel \FB, w17, w18, hi + + + // green + and w6, \DREG, #(0x3F<<5) + mul w6, w7, w6 + lsr w16, \SRC, #(8+2) + and w16, w16, #0x3F + add w6, w16, w6, lsr #(5+8) + cmp w6, #0x3F + orr w17, \FB, #(0x3F<<5) + orr w18, \FB, w6, lsl #5 + csel \FB, w17, w18, hi + + // blue + and w16, \DREG, #0x1F + mul w16, w7, w16 + lsr w6, \SRC, #(8+8+3) + and w6, w6, #0x1F + add w16, w6, w16, lsr #8 + cmp w16, #0x1F + orr w17, \FB, #0x1F + orr w18, \FB, w16 + csel \FB, w17, w18, hi + +.endif // End of blending even pixel + +.endm // End of pixel macro + + +// x0: dst ptr +// x1: src ptr +// w2: count +// w3: d +// w4: s0 +// w5: s1 +// w6: pixel +// w7: pixel +// w8: free +// w9: free +// w10: free +// w11: free +// w12: scratch +// w14: pixel + +scanline_t32cb16blend_arm64: + + // align DST to 32 bits + tst x0, #0x3 + b.eq aligned + subs w2, w2, #1 + b.lo return + +last: + ldr w4, [x1], #4 + ldrh w3, [x0] + pixel w3, w4, w12, 0 + strh w12, [x0], #2 + +aligned: + subs w2, w2, #2 + b.lo 9f + + // The main loop is unrolled twice and processes 4 pixels +8: + ldp w4,w5, [x1], #8 + add x0, x0, #4 + // it's all zero, skip this pixel + orr w3, w4, w5 + cbz w3, 7f + + // load the destination + ldr w3, [x0, #-4] + // stream the destination + pixel w3, w4, w12, 0 + pixel w3, w5, w12, 1 + str w12, [x0, #-4] + + // 2nd iteration of the loop, don't stream anything + subs w2, w2, #2 + csel w4, w5, w4, lt + blt 9f + ldp w4,w5, [x1], #8 + add x0, x0, #4 + orr w3, w4, w5 + cbz w3, 7f + ldr w3, [x0, #-4] + pixel w3, w4, w12, 0 + pixel w3, w5, w12, 1 + str w12, [x0, #-4] + +7: subs w2, w2, #2 + bhs 8b + mov w4, w5 + +9: adds w2, w2, #1 + b.lo return + b last + +return: + ret diff --git a/libpixelflinger/buffer.cpp b/libpixelflinger/buffer.cpp index af7356b..cbdab5a 100644 --- a/libpixelflinger/buffer.cpp +++ b/libpixelflinger/buffer.cpp @@ -93,7 +93,7 @@ void ggl_pick_texture(context_t* c) gen.width = s.width; gen.height = s.height; gen.stride = s.stride; - gen.data = int32_t(s.data); + gen.data = uintptr_t(s.data); } } diff --git a/libpixelflinger/codeflinger/ARMAssembler.cpp b/libpixelflinger/codeflinger/ARMAssembler.cpp index 607ed3c..92243da 100644 --- a/libpixelflinger/codeflinger/ARMAssembler.cpp +++ b/libpixelflinger/codeflinger/ARMAssembler.cpp @@ -99,8 +99,8 @@ void ARMAssembler::disassemble(const char* name) if (comment >= 0) { printf("; %s\n", mComments.valueAt(comment)); } - printf("%08x: %08x ", int(i), int(i[0])); - ::disassemble((u_int)i); + printf("%08x: %08x ", uintptr_t(i), int(i[0])); + ::disassemble((uintptr_t)i); i++; } } @@ -186,7 +186,7 @@ int ARMAssembler::generate(const char* name) #if defined(WITH_LIB_HARDWARE) if (__builtin_expect(mQemuTracing, 0)) { - int err = qemu_add_mapping(int(base()), name); + int err = qemu_add_mapping(uintptr_t(base()), name); mQemuTracing = (err >= 0); } #endif diff --git a/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp b/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp index 073633c..5041999 100644 --- a/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp +++ b/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp @@ -61,6 +61,29 @@ uint32_t ARMAssemblerInterface::__immed8_pre(int32_t immed8, int W) ((W&1)<<21) | (((offset&0xF0)<<4)|(offset&0xF)); } +// The following four functions are required for address manipulation +// These are virtual functions, which can be overridden by architectures +// that need special handling of address values (e.g. 64-bit arch) +void ARMAssemblerInterface::ADDR_LDR(int cc, int Rd, + int Rn, uint32_t offset) +{ + LDR(cc, Rd, Rn, offset); +} +void ARMAssemblerInterface::ADDR_STR(int cc, int Rd, + int Rn, uint32_t offset) +{ + STR(cc, Rd, Rn, offset); +} +void ARMAssemblerInterface::ADDR_ADD(int cc, int s, + int Rd, int Rn, uint32_t Op2) +{ + dataProcessing(opADD, cc, s, Rd, Rn, Op2); +} +void ARMAssemblerInterface::ADDR_SUB(int cc, int s, + int Rd, int Rn, uint32_t Op2) +{ + dataProcessing(opSUB, cc, s, Rd, Rn, Op2); +} }; // namespace android diff --git a/libpixelflinger/codeflinger/ARMAssemblerInterface.h b/libpixelflinger/codeflinger/ARMAssemblerInterface.h index 9991980..40cbfcf 100644 --- a/libpixelflinger/codeflinger/ARMAssemblerInterface.h +++ b/libpixelflinger/codeflinger/ARMAssemblerInterface.h @@ -63,7 +63,7 @@ public: }; enum { - CODEGEN_ARCH_ARM = 1, CODEGEN_ARCH_MIPS + CODEGEN_ARCH_ARM = 1, CODEGEN_ARCH_MIPS, CODEGEN_ARCH_ARM64 }; // ----------------------------------------------------------------------- @@ -331,6 +331,16 @@ public: inline void SMLAWT(int cc, int Rd, int Rm, int Rs, int Rn) { SMLAW(cc, yT, Rd, Rm, Rs, Rn); } + + // Address loading/storing/manipulation + virtual void ADDR_LDR(int cc, int Rd, + int Rn, uint32_t offset = __immed12_pre(0)); + virtual void ADDR_STR (int cc, int Rd, + int Rn, uint32_t offset = __immed12_pre(0)); + virtual void ADDR_ADD(int cc, int s, int Rd, + int Rn, uint32_t Op2); + virtual void ADDR_SUB(int cc, int s, int Rd, + int Rn, uint32_t Op2); }; }; // namespace android diff --git a/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp b/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp index 1c7bc76..816de48 100644 --- a/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp +++ b/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp @@ -294,5 +294,18 @@ void ARMAssemblerProxy::UBFX(int cc, int Rd, int Rn, int lsb, int width) { mTarget->UBFX(cc, Rd, Rn, lsb, width); } +void ARMAssemblerProxy::ADDR_LDR(int cc, int Rd, int Rn, uint32_t offset) { + mTarget->ADDR_LDR(cc, Rd, Rn, offset); +} +void ARMAssemblerProxy::ADDR_STR(int cc, int Rd, int Rn, uint32_t offset) { + mTarget->ADDR_STR(cc, Rd, Rn, offset); +} +void ARMAssemblerProxy::ADDR_ADD(int cc, int s, int Rd, int Rn, uint32_t Op2){ + mTarget->ADDR_ADD(cc, s, Rd, Rn, Op2); +} +void ARMAssemblerProxy::ADDR_SUB(int cc, int s, int Rd, int Rn, uint32_t Op2){ + mTarget->ADDR_SUB(cc, s, Rd, Rn, Op2); +} + }; // namespace android diff --git a/libpixelflinger/codeflinger/ARMAssemblerProxy.h b/libpixelflinger/codeflinger/ARMAssemblerProxy.h index 70cb464..b852794 100644 --- a/libpixelflinger/codeflinger/ARMAssemblerProxy.h +++ b/libpixelflinger/codeflinger/ARMAssemblerProxy.h @@ -146,6 +146,15 @@ public: virtual void UXTB16(int cc, int Rd, int Rm, int rotate); virtual void UBFX(int cc, int Rd, int Rn, int lsb, int width); + virtual void ADDR_LDR(int cc, int Rd, + int Rn, uint32_t offset = __immed12_pre(0)); + virtual void ADDR_STR (int cc, int Rd, + int Rn, uint32_t offset = __immed12_pre(0)); + virtual void ADDR_ADD(int cc, int s, int Rd, + int Rn, uint32_t Op2); + virtual void ADDR_SUB(int cc, int s, int Rd, + int Rn, uint32_t Op2); + private: ARMAssemblerInterface* mTarget; }; diff --git a/libpixelflinger/codeflinger/Arm64Assembler.cpp b/libpixelflinger/codeflinger/Arm64Assembler.cpp new file mode 100644 index 0000000..f37072a --- /dev/null +++ b/libpixelflinger/codeflinger/Arm64Assembler.cpp @@ -0,0 +1,1242 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#define LOG_TAG "ArmToArm64Assembler" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <cutils/log.h> +#include <cutils/properties.h> +#include <private/pixelflinger/ggl_context.h> + +#include "codeflinger/Arm64Assembler.h" +#include "codeflinger/CodeCache.h" +#include "codeflinger/Arm64Disassembler.h" + + +/* +** -------------------------------------------- +** Support for Arm64 in GGLAssembler JIT +** -------------------------------------------- +** +** Approach +** - GGLAssembler and associated files are largely un-changed. +** - A translator class maps ArmAssemblerInterface calls to +** generate Arm64 instructions. +** +** ---------------------- +** ArmToArm64Assembler +** ---------------------- +** +** - Subclassed from ArmAssemblerInterface +** +** - Translates each ArmAssemblerInterface call to generate +** one or more Arm64 instructions as necessary. +** +** - Does not implement ArmAssemblerInterface portions unused by GGLAssembler +** It calls NOT_IMPLEMENTED() for such cases, which in turn logs +** a fatal message. +** +** - Uses A64_.. series of functions to generate instruction machine code +** for Arm64 instructions. These functions also log the instruction +** to LOG, if ARM64_ASM_DEBUG define is set to 1 +** +** - Dumps machine code and eqvt assembly if "debug.pf.disasm" option is set +** It uses arm64_disassemble to perform disassembly +** +** - Uses register 13 (SP in ARM), 15 (PC in ARM), 16, 17 for storing +** intermediate results. GGLAssembler does not use SP and PC as these +** registers are marked as reserved. The temporary registers are not +** saved/restored on stack as these are caller-saved registers in Arm64 +** +** - Uses CSEL instruction to support conditional execution. The result is +** stored in a temporary register and then copied to the target register +** if the condition is true. +** +** - In the case of conditional data transfer instructions, conditional +** branch is used to skip over instruction, if the condition is false +** +** - Wherever possible, immediate values are transferred to temporary +** register prior to processing. This simplifies overall implementation +** as instructions requiring immediate values are converted to +** move immediate instructions followed by register-register instruction. +** +** -------------------------------------------- +** ArmToArm64Assembler unit test bench +** -------------------------------------------- +** +** - Tests ArmToArm64Assembler interface for all the possible +** ways in which GGLAssembler uses ArmAssemblerInterface interface. +** +** - Uses test jacket (written in assembly) to set the registers, +** condition flags prior to calling generated instruction. It also +** copies registers and flags at the end of execution. Caller then +** checks if generated code performed correct operation based on +** output registers and flags. +** +** - Broadly contains three type of tests, (i) data operation tests +** (ii) data transfer tests and (iii) LDM/STM tests. +** +** ---------------------- +** Arm64 disassembler +** ---------------------- +** - This disassembler disassembles only those machine codes which can be +** generated by ArmToArm64Assembler. It has a unit testbench which +** tests all the instructions supported by the disassembler. +** +** ------------------------------------------------------------------ +** ARMAssembler/ARMAssemblerInterface/ARMAssemblerProxy changes +** ------------------------------------------------------------------ +** +** - In existing code, addresses were being handled as 32 bit values at +** certain places. +** +** - Added a new set of functions for address load/store/manipulation. +** These are ADDR_LDR, ADDR_STR, ADDR_ADD, ADDR_SUB and they map to +** default 32 bit implementations in ARMAssemblerInterface. +** +** - ArmToArm64Assembler maps these functions to appropriate 64 bit +** functions. +** +** ---------------------- +** GGLAssembler changes +** ---------------------- +** - Since ArmToArm64Assembler can generate 4 Arm64 instructions for +** each call in worst case, the memory required is set to 4 times +** ARM memory +** +** - Address load/store/manipulation were changed to use new functions +** added in the ARMAssemblerInterface. +** +*/ + + +#define NOT_IMPLEMENTED() LOG_FATAL("Arm instruction %s not yet implemented\n", __func__) + +#define ARM64_ASM_DEBUG 0 + +#if ARM64_ASM_DEBUG + #define LOG_INSTR(...) ALOGD("\t" __VA_ARGS__) + #define LOG_LABEL(...) ALOGD(__VA_ARGS__) +#else + #define LOG_INSTR(...) ((void)0) + #define LOG_LABEL(...) ((void)0) +#endif + +namespace android { + +static const char* shift_codes[] = +{ + "LSL", "LSR", "ASR", "ROR" +}; +static const char *cc_codes[] = +{ + "EQ", "NE", "CS", "CC", "MI", + "PL", "VS", "VC", "HI", "LS", + "GE", "LT", "GT", "LE", "AL", "NV" +}; + +ArmToArm64Assembler::ArmToArm64Assembler(const sp<Assembly>& assembly) + : ARMAssemblerInterface(), + mAssembly(assembly) +{ + mBase = mPC = (uint32_t *)assembly->base(); + mDuration = ggl_system_time(); + mZeroReg = 13; + mTmpReg1 = 15; + mTmpReg2 = 16; + mTmpReg3 = 17; +} + +ArmToArm64Assembler::ArmToArm64Assembler(void *base) + : ARMAssemblerInterface(), mAssembly(NULL) +{ + mBase = mPC = (uint32_t *)base; + mDuration = ggl_system_time(); + // Regs 13, 15, 16, 17 are used as temporary registers + mZeroReg = 13; + mTmpReg1 = 15; + mTmpReg2 = 16; + mTmpReg3 = 17; +} + +ArmToArm64Assembler::~ArmToArm64Assembler() +{ +} + +uint32_t* ArmToArm64Assembler::pc() const +{ + return mPC; +} + +uint32_t* ArmToArm64Assembler::base() const +{ + return mBase; +} + +void ArmToArm64Assembler::reset() +{ + if(mAssembly == NULL) + mPC = mBase; + else + mBase = mPC = (uint32_t *)mAssembly->base(); + mBranchTargets.clear(); + mLabels.clear(); + mLabelsInverseMapping.clear(); + mComments.clear(); +#if ARM64_ASM_DEBUG + ALOGI("RESET\n"); +#endif +} + +int ArmToArm64Assembler::getCodegenArch() +{ + return CODEGEN_ARCH_ARM64; +} + +// ---------------------------------------------------------------------------- + +void ArmToArm64Assembler::disassemble(const char* name) +{ + if(name) + { + printf("%s:\n", name); + } + size_t count = pc()-base(); + uint32_t* i = base(); + while (count--) + { + ssize_t label = mLabelsInverseMapping.indexOfKey(i); + if (label >= 0) + { + printf("%s:\n", mLabelsInverseMapping.valueAt(label)); + } + ssize_t comment = mComments.indexOfKey(i); + if (comment >= 0) + { + printf("; %s\n", mComments.valueAt(comment)); + } + printf("%p: %08x ", i, uint32_t(i[0])); + { + char instr[256]; + ::arm64_disassemble(*i, instr); + printf("%s\n", instr); + } + i++; + } +} + +void ArmToArm64Assembler::comment(const char* string) +{ + mComments.add(mPC, string); + LOG_INSTR("//%s\n", string); +} + +void ArmToArm64Assembler::label(const char* theLabel) +{ + mLabels.add(theLabel, mPC); + mLabelsInverseMapping.add(mPC, theLabel); + LOG_LABEL("%s:\n", theLabel); +} + +void ArmToArm64Assembler::B(int cc, const char* label) +{ + mBranchTargets.add(branch_target_t(label, mPC)); + LOG_INSTR("B%s %s\n", cc_codes[cc], label ); + *mPC++ = (0x54 << 24) | cc; +} + +void ArmToArm64Assembler::BL(int cc, const char* label) +{ + NOT_IMPLEMENTED(); //Not Required +} + +// ---------------------------------------------------------------------------- +//Prolog/Epilog & Generate... +// ---------------------------------------------------------------------------- + +void ArmToArm64Assembler::prolog() +{ + // write prolog code + mPrologPC = mPC; + *mPC++ = A64_MOVZ_X(mZeroReg,0,0); +} + +void ArmToArm64Assembler::epilog(uint32_t touched) +{ + // write epilog code + static const int XLR = 30; + *mPC++ = A64_RET(XLR); +} + +int ArmToArm64Assembler::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); + *bt.pc |= (offset & 0x7FFFF) << 5; + } + + if(mAssembly != NULL) + mAssembly->resize( int(pc()-base())*4 ); + + // the instruction cache is flushed by CodeCache + const int64_t duration = ggl_system_time() - mDuration; + const char * const format = "generated %s (%d ins) at [%p:%p] in %ld ns\n"; + ALOGI(format, name, int(pc()-base()), base(), pc(), duration); + + + char value[PROPERTY_VALUE_MAX]; + property_get("debug.pf.disasm", value, "0"); + if (atoi(value) != 0) + { + printf(format, name, int(pc()-base()), base(), pc(), duration); + disassemble(name); + } + return NO_ERROR; +} + +uint32_t* ArmToArm64Assembler::pcForLabel(const char* label) +{ + return mLabels.valueFor(label); +} + +// ---------------------------------------------------------------------------- +// Data Processing... +// ---------------------------------------------------------------------------- +void ArmToArm64Assembler::dataProcessingCommon(int opcode, + int s, int Rd, int Rn, uint32_t Op2) +{ + if(opcode != opSUB && s == 1) + { + NOT_IMPLEMENTED(); //Not required + return; + } + + if(opcode != opSUB && opcode != opADD && opcode != opAND && + opcode != opORR && opcode != opMVN) + { + NOT_IMPLEMENTED(); //Not required + return; + } + + if(Op2 == OPERAND_REG_IMM && mAddrMode.reg_imm_shift > 31) + { + NOT_IMPLEMENTED(); + return; + } + + //Store immediate in temporary register and convert + //immediate operation into register operation + if(Op2 == OPERAND_IMM) + { + int imm = mAddrMode.immediate; + *mPC++ = A64_MOVZ_W(mTmpReg2, imm & 0x0000FFFF, 0); + *mPC++ = A64_MOVK_W(mTmpReg2, (imm >> 16) & 0x0000FFFF, 16); + Op2 = mTmpReg2; + } + + + { + uint32_t shift; + uint32_t amount; + uint32_t Rm; + + if(Op2 == OPERAND_REG_IMM) + { + shift = mAddrMode.reg_imm_type; + amount = mAddrMode.reg_imm_shift; + Rm = mAddrMode.reg_imm_Rm; + } + else if(Op2 < OPERAND_REG) + { + shift = 0; + amount = 0; + Rm = Op2; + } + else + { + NOT_IMPLEMENTED(); //Not required + return; + } + + switch(opcode) + { + case opADD: *mPC++ = A64_ADD_W(Rd, Rn, Rm, shift, amount); break; + case opAND: *mPC++ = A64_AND_W(Rd, Rn, Rm, shift, amount); break; + case opORR: *mPC++ = A64_ORR_W(Rd, Rn, Rm, shift, amount); break; + case opMVN: *mPC++ = A64_ORN_W(Rd, Rn, Rm, shift, amount); break; + case opSUB: *mPC++ = A64_SUB_W(Rd, Rn, Rm, shift, amount, s);break; + }; + + } +} + +void ArmToArm64Assembler::dataProcessing(int opcode, int cc, + int s, int Rd, int Rn, uint32_t Op2) +{ + uint32_t Wd; + + if(cc != AL) + Wd = mTmpReg1; + else + Wd = Rd; + + if(opcode == opADD || opcode == opAND || opcode == opORR ||opcode == opSUB) + { + dataProcessingCommon(opcode, s, Wd, Rn, Op2); + } + else if(opcode == opCMP) + { + dataProcessingCommon(opSUB, 1, mTmpReg3, Rn, Op2); + } + else if(opcode == opRSB) + { + dataProcessingCommon(opSUB, s, Wd, Rn, Op2); + dataProcessingCommon(opSUB, s, Wd, mZeroReg, Wd); + } + else if(opcode == opMOV) + { + dataProcessingCommon(opORR, 0, Wd, mZeroReg, Op2); + if(s == 1) + { + dataProcessingCommon(opSUB, 1, mTmpReg3, Wd, mZeroReg); + } + } + else if(opcode == opMVN) + { + dataProcessingCommon(opMVN, s, Wd, mZeroReg, Op2); + } + else if(opcode == opBIC) + { + dataProcessingCommon(opMVN, s, mTmpReg3, mZeroReg, Op2); + dataProcessingCommon(opAND, s, Wd, Rn, mTmpReg3); + } + else + { + NOT_IMPLEMENTED(); + return; + } + + if(cc != AL) + { + *mPC++ = A64_CSEL_W(Rd, mTmpReg1, Rd, cc); + } +} +// ---------------------------------------------------------------------------- +// Address Processing... +// ---------------------------------------------------------------------------- + +void ArmToArm64Assembler::ADDR_ADD(int cc, + int s, int Rd, int Rn, uint32_t Op2) +{ + if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required + if(s != 0) { NOT_IMPLEMENTED(); return;} //Not required + + + if(Op2 == OPERAND_REG_IMM && mAddrMode.reg_imm_type == LSL) + { + int Rm = mAddrMode.reg_imm_Rm; + int amount = mAddrMode.reg_imm_shift; + *mPC++ = A64_ADD_X_Wm_SXTW(Rd, Rn, Rm, amount); + } + else if(Op2 < OPERAND_REG) + { + int Rm = Op2; + int amount = 0; + *mPC++ = A64_ADD_X_Wm_SXTW(Rd, Rn, Rm, amount); + } + else if(Op2 == OPERAND_IMM) + { + int imm = mAddrMode.immediate; + *mPC++ = A64_MOVZ_W(mTmpReg1, imm & 0x0000FFFF, 0); + *mPC++ = A64_MOVK_W(mTmpReg1, (imm >> 16) & 0x0000FFFF, 16); + + int Rm = mTmpReg1; + int amount = 0; + *mPC++ = A64_ADD_X_Wm_SXTW(Rd, Rn, Rm, amount); + } + else + { + NOT_IMPLEMENTED(); //Not required + } +} + +void ArmToArm64Assembler::ADDR_SUB(int cc, + int s, int Rd, int Rn, uint32_t Op2) +{ + if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required + if(s != 0) { NOT_IMPLEMENTED(); return;} //Not required + + if(Op2 == OPERAND_REG_IMM && mAddrMode.reg_imm_type == LSR) + { + *mPC++ = A64_ADD_W(mTmpReg1, mZeroReg, mAddrMode.reg_imm_Rm, + LSR, mAddrMode.reg_imm_shift); + *mPC++ = A64_SUB_X_Wm_SXTW(Rd, Rn, mTmpReg1, 0); + } + else + { + NOT_IMPLEMENTED(); //Not required + } +} + +// ---------------------------------------------------------------------------- +// multiply... +// ---------------------------------------------------------------------------- +void ArmToArm64Assembler::MLA(int cc, int s,int Rd, int Rm, int Rs, int Rn) +{ + if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required + + *mPC++ = A64_MADD_W(Rd, Rm, Rs, Rn); + if(s == 1) + dataProcessingCommon(opSUB, 1, mTmpReg1, Rd, mZeroReg); +} +void ArmToArm64Assembler::MUL(int cc, int s, int Rd, int Rm, int Rs) +{ + if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required + if(s != 0) { NOT_IMPLEMENTED(); return;} //Not required + *mPC++ = A64_MADD_W(Rd, Rm, Rs, mZeroReg); +} +void ArmToArm64Assembler::UMULL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs) +{ + NOT_IMPLEMENTED(); //Not required +} +void ArmToArm64Assembler::UMUAL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs) +{ + NOT_IMPLEMENTED(); //Not required +} +void ArmToArm64Assembler::SMULL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs) +{ + NOT_IMPLEMENTED(); //Not required +} +void ArmToArm64Assembler::SMUAL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs) +{ + NOT_IMPLEMENTED(); //Not required +} + +// ---------------------------------------------------------------------------- +// branches relative to PC... +// ---------------------------------------------------------------------------- +void ArmToArm64Assembler::B(int cc, uint32_t* pc){ + NOT_IMPLEMENTED(); //Not required +} + +void ArmToArm64Assembler::BL(int cc, uint32_t* pc){ + NOT_IMPLEMENTED(); //Not required +} + +void ArmToArm64Assembler::BX(int cc, int Rn){ + NOT_IMPLEMENTED(); //Not required +} + +// ---------------------------------------------------------------------------- +// data transfer... +// ---------------------------------------------------------------------------- +enum dataTransferOp +{ + opLDR,opLDRB,opLDRH,opSTR,opSTRB,opSTRH +}; + +void ArmToArm64Assembler::dataTransfer(int op, int cc, + int Rd, int Rn, uint32_t op_type, uint32_t size) +{ + const int XSP = 31; + if(Rn == SP) + Rn = XSP; + + if(op_type == OPERAND_IMM) + { + int addrReg; + int imm = mAddrMode.immediate; + if(imm >= 0 && imm < (1<<12)) + *mPC++ = A64_ADD_IMM_X(mTmpReg1, mZeroReg, imm, 0); + else if(imm < 0 && -imm < (1<<12)) + *mPC++ = A64_SUB_IMM_X(mTmpReg1, mZeroReg, -imm, 0); + else + { + NOT_IMPLEMENTED(); + return; + } + + addrReg = Rn; + if(mAddrMode.preindex == true || mAddrMode.postindex == true) + { + *mPC++ = A64_ADD_X(mTmpReg2, addrReg, mTmpReg1); + if(mAddrMode.preindex == true) + addrReg = mTmpReg2; + } + + if(cc != AL) + *mPC++ = A64_B_COND(cc^1, 8); + + *mPC++ = A64_LDRSTR_Wm_SXTW_0(op, size, Rd, addrReg, mZeroReg); + + if(mAddrMode.writeback == true) + *mPC++ = A64_CSEL_X(Rn, mTmpReg2, Rn, cc); + } + else if(op_type == OPERAND_REG_OFFSET) + { + if(cc != AL) + *mPC++ = A64_B_COND(cc^1, 8); + *mPC++ = A64_LDRSTR_Wm_SXTW_0(op, size, Rd, Rn, mAddrMode.reg_offset); + + } + else if(op_type > OPERAND_UNSUPPORTED) + { + if(cc != AL) + *mPC++ = A64_B_COND(cc^1, 8); + *mPC++ = A64_LDRSTR_Wm_SXTW_0(op, size, Rd, Rn, mZeroReg); + } + else + { + NOT_IMPLEMENTED(); // Not required + } + return; + +} +void ArmToArm64Assembler::ADDR_LDR(int cc, int Rd, int Rn, uint32_t op_type) +{ + return dataTransfer(opLDR, cc, Rd, Rn, op_type, 64); +} +void ArmToArm64Assembler::ADDR_STR(int cc, int Rd, int Rn, uint32_t op_type) +{ + return dataTransfer(opSTR, cc, Rd, Rn, op_type, 64); +} +void ArmToArm64Assembler::LDR(int cc, int Rd, int Rn, uint32_t op_type) +{ + return dataTransfer(opLDR, cc, Rd, Rn, op_type); +} +void ArmToArm64Assembler::LDRB(int cc, int Rd, int Rn, uint32_t op_type) +{ + return dataTransfer(opLDRB, cc, Rd, Rn, op_type); +} +void ArmToArm64Assembler::STR(int cc, int Rd, int Rn, uint32_t op_type) +{ + return dataTransfer(opSTR, cc, Rd, Rn, op_type); +} + +void ArmToArm64Assembler::STRB(int cc, int Rd, int Rn, uint32_t op_type) +{ + return dataTransfer(opSTRB, cc, Rd, Rn, op_type); +} + +void ArmToArm64Assembler::LDRH(int cc, int Rd, int Rn, uint32_t op_type) +{ + return dataTransfer(opLDRH, cc, Rd, Rn, op_type); +} +void ArmToArm64Assembler::LDRSB(int cc, int Rd, int Rn, uint32_t offset) +{ + NOT_IMPLEMENTED(); //Not required +} +void ArmToArm64Assembler::LDRSH(int cc, int Rd, int Rn, uint32_t offset) +{ + NOT_IMPLEMENTED(); //Not required +} + +void ArmToArm64Assembler::STRH(int cc, int Rd, int Rn, uint32_t op_type) +{ + return dataTransfer(opSTRH, cc, Rd, Rn, op_type); +} + +// ---------------------------------------------------------------------------- +// block data transfer... +// ---------------------------------------------------------------------------- +void ArmToArm64Assembler::LDM(int cc, int dir, + int Rn, int W, uint32_t reg_list) +{ + const int XSP = 31; + if(cc != AL || dir != IA || W == 0 || Rn != SP) + { + NOT_IMPLEMENTED(); + return; + } + + for(int i = 0; i < 32; ++i) + { + if((reg_list & (1 << i))) + { + int reg = i; + int size = 16; + *mPC++ = A64_LDR_IMM_PostIndex(reg, XSP, size); + } + } +} + +void ArmToArm64Assembler::STM(int cc, int dir, + int Rn, int W, uint32_t reg_list) +{ + const int XSP = 31; + if(cc != AL || dir != DB || W == 0 || Rn != SP) + { + NOT_IMPLEMENTED(); + return; + } + + for(int i = 31; i >= 0; --i) + { + if((reg_list & (1 << i))) + { + int size = -16; + int reg = i; + *mPC++ = A64_STR_IMM_PreIndex(reg, XSP, size); + } + } +} + +// ---------------------------------------------------------------------------- +// special... +// ---------------------------------------------------------------------------- +void ArmToArm64Assembler::SWP(int cc, int Rn, int Rd, int Rm) +{ + NOT_IMPLEMENTED(); //Not required +} +void ArmToArm64Assembler::SWPB(int cc, int Rn, int Rd, int Rm) +{ + NOT_IMPLEMENTED(); //Not required +} +void ArmToArm64Assembler::SWI(int cc, uint32_t comment) +{ + NOT_IMPLEMENTED(); //Not required +} + +// ---------------------------------------------------------------------------- +// DSP instructions... +// ---------------------------------------------------------------------------- +void ArmToArm64Assembler::PLD(int Rn, uint32_t offset) { + NOT_IMPLEMENTED(); //Not required +} + +void ArmToArm64Assembler::CLZ(int cc, int Rd, int Rm) +{ + NOT_IMPLEMENTED(); //Not required +} + +void ArmToArm64Assembler::QADD(int cc, int Rd, int Rm, int Rn) +{ + NOT_IMPLEMENTED(); //Not required +} + +void ArmToArm64Assembler::QDADD(int cc, int Rd, int Rm, int Rn) +{ + NOT_IMPLEMENTED(); //Not required +} + +void ArmToArm64Assembler::QSUB(int cc, int Rd, int Rm, int Rn) +{ + NOT_IMPLEMENTED(); //Not required +} + +void ArmToArm64Assembler::QDSUB(int cc, int Rd, int Rm, int Rn) +{ + NOT_IMPLEMENTED(); //Not required +} + +// ---------------------------------------------------------------------------- +// 16 x 16 multiplication +// ---------------------------------------------------------------------------- +void ArmToArm64Assembler::SMUL(int cc, int xy, + int Rd, int Rm, int Rs) +{ + if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required + + if (xy & xyTB) + *mPC++ = A64_SBFM_W(mTmpReg1, Rm, 16, 31); + else + *mPC++ = A64_SBFM_W(mTmpReg1, Rm, 0, 15); + + if (xy & xyBT) + *mPC++ = A64_SBFM_W(mTmpReg2, Rs, 16, 31); + else + *mPC++ = A64_SBFM_W(mTmpReg2, Rs, 0, 15); + + *mPC++ = A64_MADD_W(Rd,mTmpReg1,mTmpReg2, mZeroReg); +} +// ---------------------------------------------------------------------------- +// 32 x 16 multiplication +// ---------------------------------------------------------------------------- +void ArmToArm64Assembler::SMULW(int cc, int y, int Rd, int Rm, int Rs) +{ + if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required + + if (y & yT) + *mPC++ = A64_SBFM_W(mTmpReg1, Rs, 16, 31); + else + *mPC++ = A64_SBFM_W(mTmpReg1, Rs, 0, 15); + + *mPC++ = A64_SBFM_W(mTmpReg2, Rm, 0, 31); + *mPC++ = A64_SMADDL(mTmpReg3,mTmpReg1,mTmpReg2, mZeroReg); + *mPC++ = A64_UBFM_X(Rd,mTmpReg3, 16, 47); +} +// ---------------------------------------------------------------------------- +// 16 x 16 multiplication and accumulate +// ---------------------------------------------------------------------------- +void ArmToArm64Assembler::SMLA(int cc, int xy, int Rd, int Rm, int Rs, int Rn) +{ + if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required + if(xy != xyBB) { NOT_IMPLEMENTED(); return;} //Not required + + *mPC++ = A64_SBFM_W(mTmpReg1, Rm, 0, 15); + *mPC++ = A64_SBFM_W(mTmpReg2, Rs, 0, 15); + *mPC++ = A64_MADD_W(Rd, mTmpReg1, mTmpReg2, Rn); +} + +void ArmToArm64Assembler::SMLAL(int cc, int xy, + int RdHi, int RdLo, int Rs, int Rm) +{ + NOT_IMPLEMENTED(); //Not required + return; +} + +void ArmToArm64Assembler::SMLAW(int cc, int y, + int Rd, int Rm, int Rs, int Rn) +{ + NOT_IMPLEMENTED(); //Not required + return; +} + +// ---------------------------------------------------------------------------- +// Byte/half word extract and extend +// ---------------------------------------------------------------------------- +void ArmToArm64Assembler::UXTB16(int cc, int Rd, int Rm, int rotate) +{ + if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required + + *mPC++ = A64_EXTR_W(mTmpReg1, Rm, Rm, rotate * 8); + + uint32_t imm = 0x00FF00FF; + *mPC++ = A64_MOVZ_W(mTmpReg2, imm & 0xFFFF, 0); + *mPC++ = A64_MOVK_W(mTmpReg2, (imm >> 16) & 0x0000FFFF, 16); + *mPC++ = A64_AND_W(Rd,mTmpReg1, mTmpReg2); +} + +// ---------------------------------------------------------------------------- +// Bit manipulation +// ---------------------------------------------------------------------------- +void ArmToArm64Assembler::UBFX(int cc, int Rd, int Rn, int lsb, int width) +{ + if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required + *mPC++ = A64_UBFM_W(Rd, Rn, lsb, lsb + width - 1); +} +// ---------------------------------------------------------------------------- +// Shifters... +// ---------------------------------------------------------------------------- +int ArmToArm64Assembler::buildImmediate( + uint32_t immediate, uint32_t& rot, uint32_t& imm) +{ + rot = 0; + imm = immediate; + return 0; // Always true +} + + +bool ArmToArm64Assembler::isValidImmediate(uint32_t immediate) +{ + uint32_t rot, imm; + return buildImmediate(immediate, rot, imm) == 0; +} + +uint32_t ArmToArm64Assembler::imm(uint32_t immediate) +{ + mAddrMode.immediate = immediate; + mAddrMode.writeback = false; + mAddrMode.preindex = false; + mAddrMode.postindex = false; + return OPERAND_IMM; + +} + +uint32_t ArmToArm64Assembler::reg_imm(int Rm, int type, uint32_t shift) +{ + mAddrMode.reg_imm_Rm = Rm; + mAddrMode.reg_imm_type = type; + mAddrMode.reg_imm_shift = shift; + return OPERAND_REG_IMM; +} + +uint32_t ArmToArm64Assembler::reg_rrx(int Rm) +{ + NOT_IMPLEMENTED(); + return OPERAND_UNSUPPORTED; +} + +uint32_t ArmToArm64Assembler::reg_reg(int Rm, int type, int Rs) +{ + NOT_IMPLEMENTED(); //Not required + return OPERAND_UNSUPPORTED; +} +// ---------------------------------------------------------------------------- +// Addressing modes... +// ---------------------------------------------------------------------------- +uint32_t ArmToArm64Assembler::immed12_pre(int32_t immed12, int W) +{ + mAddrMode.immediate = immed12; + mAddrMode.writeback = W; + mAddrMode.preindex = true; + mAddrMode.postindex = false; + return OPERAND_IMM; +} + +uint32_t ArmToArm64Assembler::immed12_post(int32_t immed12) +{ + mAddrMode.immediate = immed12; + mAddrMode.writeback = true; + mAddrMode.preindex = false; + mAddrMode.postindex = true; + return OPERAND_IMM; +} + +uint32_t ArmToArm64Assembler::reg_scale_pre(int Rm, int type, + uint32_t shift, int W) +{ + if(type != 0 || shift != 0 || W != 0) + { + NOT_IMPLEMENTED(); //Not required + return OPERAND_UNSUPPORTED; + } + else + { + mAddrMode.reg_offset = Rm; + return OPERAND_REG_OFFSET; + } +} + +uint32_t ArmToArm64Assembler::reg_scale_post(int Rm, int type, uint32_t shift) +{ + NOT_IMPLEMENTED(); //Not required + return OPERAND_UNSUPPORTED; +} + +uint32_t ArmToArm64Assembler::immed8_pre(int32_t immed8, int W) +{ + mAddrMode.immediate = immed8; + mAddrMode.writeback = W; + mAddrMode.preindex = true; + mAddrMode.postindex = false; + return OPERAND_IMM; +} + +uint32_t ArmToArm64Assembler::immed8_post(int32_t immed8) +{ + mAddrMode.immediate = immed8; + mAddrMode.writeback = true; + mAddrMode.preindex = false; + mAddrMode.postindex = true; + return OPERAND_IMM; +} + +uint32_t ArmToArm64Assembler::reg_pre(int Rm, int W) +{ + if(W != 0) + { + NOT_IMPLEMENTED(); //Not required + return OPERAND_UNSUPPORTED; + } + else + { + mAddrMode.reg_offset = Rm; + return OPERAND_REG_OFFSET; + } +} + +uint32_t ArmToArm64Assembler::reg_post(int Rm) +{ + NOT_IMPLEMENTED(); //Not required + return OPERAND_UNSUPPORTED; +} + +// ---------------------------------------------------------------------------- +// A64 instructions +// ---------------------------------------------------------------------------- + +static const char * dataTransferOpName[] = +{ + "LDR","LDRB","LDRH","STR","STRB","STRH" +}; + +static const uint32_t dataTransferOpCode [] = +{ + ((0xB8u << 24) | (0x3 << 21) | (0x6 << 13) | (0x0 << 12) |(0x1 << 11)), + ((0x38u << 24) | (0x3 << 21) | (0x6 << 13) | (0x1 << 12) |(0x1 << 11)), + ((0x78u << 24) | (0x3 << 21) | (0x6 << 13) | (0x0 << 12) |(0x1 << 11)), + ((0xB8u << 24) | (0x1 << 21) | (0x6 << 13) | (0x0 << 12) |(0x1 << 11)), + ((0x38u << 24) | (0x1 << 21) | (0x6 << 13) | (0x1 << 12) |(0x1 << 11)), + ((0x78u << 24) | (0x1 << 21) | (0x6 << 13) | (0x0 << 12) |(0x1 << 11)) +}; +uint32_t ArmToArm64Assembler::A64_LDRSTR_Wm_SXTW_0(uint32_t op, + uint32_t size, uint32_t Rt, + uint32_t Rn, uint32_t Rm) +{ + if(size == 32) + { + LOG_INSTR("%s W%d, [X%d, W%d, SXTW #0]\n", + dataTransferOpName[op], Rt, Rn, Rm); + return(dataTransferOpCode[op] | (Rm << 16) | (Rn << 5) | Rt); + } + else + { + LOG_INSTR("%s X%d, [X%d, W%d, SXTW #0]\n", + dataTransferOpName[op], Rt, Rn, Rm); + return(dataTransferOpCode[op] | (0x1<<30) | (Rm<<16) | (Rn<<5)|Rt); + } +} + +uint32_t ArmToArm64Assembler::A64_STR_IMM_PreIndex(uint32_t Rt, + uint32_t Rn, int32_t simm) +{ + if(Rn == 31) + LOG_INSTR("STR W%d, [SP, #%d]!\n", Rt, simm); + else + LOG_INSTR("STR W%d, [X%d, #%d]!\n", Rt, Rn, simm); + + uint32_t imm9 = (unsigned)(simm) & 0x01FF; + return (0xB8 << 24) | (imm9 << 12) | (0x3 << 10) | (Rn << 5) | Rt; +} + +uint32_t ArmToArm64Assembler::A64_LDR_IMM_PostIndex(uint32_t Rt, + uint32_t Rn, int32_t simm) +{ + if(Rn == 31) + LOG_INSTR("LDR W%d, [SP], #%d\n",Rt,simm); + else + LOG_INSTR("LDR W%d, [X%d], #%d\n",Rt, Rn, simm); + + uint32_t imm9 = (unsigned)(simm) & 0x01FF; + return (0xB8 << 24) | (0x1 << 22) | + (imm9 << 12) | (0x1 << 10) | (Rn << 5) | Rt; + +} +uint32_t ArmToArm64Assembler::A64_ADD_X_Wm_SXTW(uint32_t Rd, + uint32_t Rn, + uint32_t Rm, + uint32_t amount) +{ + LOG_INSTR("ADD X%d, X%d, W%d, SXTW #%d\n", Rd, Rn, Rm, amount); + return ((0x8B << 24) | (0x1 << 21) |(Rm << 16) | + (0x6 << 13) | (amount << 10) | (Rn << 5) | Rd); + +} + +uint32_t ArmToArm64Assembler::A64_SUB_X_Wm_SXTW(uint32_t Rd, + uint32_t Rn, + uint32_t Rm, + uint32_t amount) +{ + LOG_INSTR("SUB X%d, X%d, W%d, SXTW #%d\n", Rd, Rn, Rm, amount); + return ((0xCB << 24) | (0x1 << 21) |(Rm << 16) | + (0x6 << 13) | (amount << 10) | (Rn << 5) | Rd); + +} + +uint32_t ArmToArm64Assembler::A64_B_COND(uint32_t cc, uint32_t offset) +{ + LOG_INSTR("B.%s #.+%d\n", cc_codes[cc], offset); + return (0x54 << 24) | ((offset/4) << 5) | (cc); + +} +uint32_t ArmToArm64Assembler::A64_ADD_X(uint32_t Rd, uint32_t Rn, + uint32_t Rm, uint32_t shift, + uint32_t amount) +{ + LOG_INSTR("ADD X%d, X%d, X%d, %s #%d\n", + Rd, Rn, Rm, shift_codes[shift], amount); + return ((0x8B << 24) | (shift << 22) | ( Rm << 16) | + (amount << 10) |(Rn << 5) | Rd); +} +uint32_t ArmToArm64Assembler::A64_ADD_IMM_X(uint32_t Rd, uint32_t Rn, + uint32_t imm, uint32_t shift) +{ + LOG_INSTR("ADD X%d, X%d, #%d, LSL #%d\n", Rd, Rn, imm, shift); + return (0x91 << 24) | ((shift/12) << 22) | (imm << 10) | (Rn << 5) | Rd; +} + +uint32_t ArmToArm64Assembler::A64_SUB_IMM_X(uint32_t Rd, uint32_t Rn, + uint32_t imm, uint32_t shift) +{ + LOG_INSTR("SUB X%d, X%d, #%d, LSL #%d\n", Rd, Rn, imm, shift); + return (0xD1 << 24) | ((shift/12) << 22) | (imm << 10) | (Rn << 5) | Rd; +} + +uint32_t ArmToArm64Assembler::A64_ADD_W(uint32_t Rd, uint32_t Rn, + uint32_t Rm, uint32_t shift, + uint32_t amount) +{ + LOG_INSTR("ADD W%d, W%d, W%d, %s #%d\n", + Rd, Rn, Rm, shift_codes[shift], amount); + return ((0x0B << 24) | (shift << 22) | ( Rm << 16) | + (amount << 10) |(Rn << 5) | Rd); +} + +uint32_t ArmToArm64Assembler::A64_SUB_W(uint32_t Rd, uint32_t Rn, + uint32_t Rm, uint32_t shift, + uint32_t amount, + uint32_t setflag) +{ + if(setflag == 0) + { + LOG_INSTR("SUB W%d, W%d, W%d, %s #%d\n", + Rd, Rn, Rm, shift_codes[shift], amount); + return ((0x4B << 24) | (shift << 22) | ( Rm << 16) | + (amount << 10) |(Rn << 5) | Rd); + } + else + { + LOG_INSTR("SUBS W%d, W%d, W%d, %s #%d\n", + Rd, Rn, Rm, shift_codes[shift], amount); + return ((0x6B << 24) | (shift << 22) | ( Rm << 16) | + (amount << 10) |(Rn << 5) | Rd); + } +} + +uint32_t ArmToArm64Assembler::A64_AND_W(uint32_t Rd, uint32_t Rn, + uint32_t Rm, uint32_t shift, + uint32_t amount) +{ + LOG_INSTR("AND W%d, W%d, W%d, %s #%d\n", + Rd, Rn, Rm, shift_codes[shift], amount); + return ((0x0A << 24) | (shift << 22) | ( Rm << 16) | + (amount << 10) |(Rn << 5) | Rd); +} + +uint32_t ArmToArm64Assembler::A64_ORR_W(uint32_t Rd, uint32_t Rn, + uint32_t Rm, uint32_t shift, + uint32_t amount) +{ + LOG_INSTR("ORR W%d, W%d, W%d, %s #%d\n", + Rd, Rn, Rm, shift_codes[shift], amount); + return ((0x2A << 24) | (shift << 22) | ( Rm << 16) | + (amount << 10) |(Rn << 5) | Rd); +} + +uint32_t ArmToArm64Assembler::A64_ORN_W(uint32_t Rd, uint32_t Rn, + uint32_t Rm, uint32_t shift, + uint32_t amount) +{ + LOG_INSTR("ORN W%d, W%d, W%d, %s #%d\n", + Rd, Rn, Rm, shift_codes[shift], amount); + return ((0x2A << 24) | (shift << 22) | (0x1 << 21) | ( Rm << 16) | + (amount << 10) |(Rn << 5) | Rd); +} + +uint32_t ArmToArm64Assembler::A64_CSEL_X(uint32_t Rd, uint32_t Rn, + uint32_t Rm, uint32_t cond) +{ + LOG_INSTR("CSEL X%d, X%d, X%d, %s\n", Rd, Rn, Rm, cc_codes[cond]); + return ((0x9A << 24)|(0x1 << 23)|(Rm << 16) |(cond << 12)| (Rn << 5) | Rd); +} + +uint32_t ArmToArm64Assembler::A64_CSEL_W(uint32_t Rd, uint32_t Rn, + uint32_t Rm, uint32_t cond) +{ + LOG_INSTR("CSEL W%d, W%d, W%d, %s\n", Rd, Rn, Rm, cc_codes[cond]); + return ((0x1A << 24)|(0x1 << 23)|(Rm << 16) |(cond << 12)| (Rn << 5) | Rd); +} + +uint32_t ArmToArm64Assembler::A64_RET(uint32_t Rn) +{ + LOG_INSTR("RET X%d\n", Rn); + return ((0xD6 << 24) | (0x1 << 22) | (0x1F << 16) | (Rn << 5)); +} + +uint32_t ArmToArm64Assembler::A64_MOVZ_X(uint32_t Rd, uint32_t imm, + uint32_t shift) +{ + LOG_INSTR("MOVZ X%d, #0x%x, LSL #%d\n", Rd, imm, shift); + return(0xD2 << 24) | (0x1 << 23) | ((shift/16) << 21) | (imm << 5) | Rd; +} + +uint32_t ArmToArm64Assembler::A64_MOVK_W(uint32_t Rd, uint32_t imm, + uint32_t shift) +{ + LOG_INSTR("MOVK W%d, #0x%x, LSL #%d\n", Rd, imm, shift); + return (0x72 << 24) | (0x1 << 23) | ((shift/16) << 21) | (imm << 5) | Rd; +} + +uint32_t ArmToArm64Assembler::A64_MOVZ_W(uint32_t Rd, uint32_t imm, + uint32_t shift) +{ + LOG_INSTR("MOVZ W%d, #0x%x, LSL #%d\n", Rd, imm, shift); + return(0x52 << 24) | (0x1 << 23) | ((shift/16) << 21) | (imm << 5) | Rd; +} + +uint32_t ArmToArm64Assembler::A64_SMADDL(uint32_t Rd, uint32_t Rn, + uint32_t Rm, uint32_t Ra) +{ + LOG_INSTR("SMADDL X%d, W%d, W%d, X%d\n",Rd, Rn, Rm, Ra); + return ((0x9B << 24) | (0x1 << 21) | (Rm << 16)|(Ra << 10)|(Rn << 5) | Rd); +} + +uint32_t ArmToArm64Assembler::A64_MADD_W(uint32_t Rd, uint32_t Rn, + uint32_t Rm, uint32_t Ra) +{ + LOG_INSTR("MADD W%d, W%d, W%d, W%d\n",Rd, Rn, Rm, Ra); + return ((0x1B << 24) | (Rm << 16) | (Ra << 10) |(Rn << 5) | Rd); +} + +uint32_t ArmToArm64Assembler::A64_SBFM_W(uint32_t Rd, uint32_t Rn, + uint32_t immr, uint32_t imms) +{ + LOG_INSTR("SBFM W%d, W%d, #%d, #%d\n", Rd, Rn, immr, imms); + return ((0x13 << 24) | (immr << 16) | (imms << 10) | (Rn << 5) | Rd); + +} +uint32_t ArmToArm64Assembler::A64_UBFM_W(uint32_t Rd, uint32_t Rn, + uint32_t immr, uint32_t imms) +{ + LOG_INSTR("UBFM W%d, W%d, #%d, #%d\n", Rd, Rn, immr, imms); + return ((0x53 << 24) | (immr << 16) | (imms << 10) | (Rn << 5) | Rd); + +} +uint32_t ArmToArm64Assembler::A64_UBFM_X(uint32_t Rd, uint32_t Rn, + uint32_t immr, uint32_t imms) +{ + LOG_INSTR("UBFM X%d, X%d, #%d, #%d\n", Rd, Rn, immr, imms); + return ((0xD3 << 24) | (0x1 << 22) | + (immr << 16) | (imms << 10) | (Rn << 5) | Rd); + +} +uint32_t ArmToArm64Assembler::A64_EXTR_W(uint32_t Rd, uint32_t Rn, + uint32_t Rm, uint32_t lsb) +{ + LOG_INSTR("EXTR W%d, W%d, W%d, #%d\n", Rd, Rn, Rm, lsb); + return (0x13 << 24)|(0x1 << 23) | (Rm << 16) | (lsb << 10)|(Rn << 5) | Rd; +} + +}; // namespace android + diff --git a/libpixelflinger/codeflinger/Arm64Assembler.h b/libpixelflinger/codeflinger/Arm64Assembler.h new file mode 100644 index 0000000..8479270 --- /dev/null +++ b/libpixelflinger/codeflinger/Arm64Assembler.h @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef ANDROID_ARMTOARM64ASSEMBLER_H +#define ANDROID_ARMTOARM64ASSEMBLER_H + +#include <stdint.h> +#include <sys/types.h> + +#include "tinyutils/Vector.h" +#include "tinyutils/KeyedVector.h" +#include "tinyutils/smartpointer.h" + +#include "tinyutils/smartpointer.h" +#include "codeflinger/ARMAssemblerInterface.h" +#include "codeflinger/CodeCache.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +class ArmToArm64Assembler : public ARMAssemblerInterface +{ +public: + ArmToArm64Assembler(const sp<Assembly>& assembly); + ArmToArm64Assembler(void *base); + virtual ~ArmToArm64Assembler(); + + uint32_t* base() const; + uint32_t* pc() const; + + + void disassemble(const char* name); + + // ------------------------------------------------------------------------ + // ARMAssemblerInterface... + // ------------------------------------------------------------------------ + + 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... + 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); + 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 ADDR_LDR(int cc, int Rd, + int Rn, uint32_t offset = 0); + virtual void ADDR_ADD(int cc, int s, int Rd, + int Rn, uint32_t Op2); + virtual void ADDR_SUB(int cc, int s, int Rd, + int Rn, uint32_t Op2); + virtual void ADDR_STR (int cc, int Rd, + int Rn, uint32_t offset = 0); + + 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); + virtual void UXTB16(int cc, int Rd, int Rm, int rotate); + virtual void UBFX(int cc, int Rd, int Rn, int lsb, int width); + +private: + ArmToArm64Assembler(const ArmToArm64Assembler& rhs); + ArmToArm64Assembler& operator = (const ArmToArm64Assembler& rhs); + + // ----------------------------------------------------------------------- + // helper functions + // ----------------------------------------------------------------------- + + void dataTransfer(int operation, int cc, int Rd, int Rn, + uint32_t operand_type, uint32_t size = 32); + void dataProcessingCommon(int opcode, int s, + int Rd, int Rn, uint32_t Op2); + + // ----------------------------------------------------------------------- + // Arm64 instructions + // ----------------------------------------------------------------------- + uint32_t A64_B_COND(uint32_t cc, uint32_t offset); + uint32_t A64_RET(uint32_t Rn); + + uint32_t A64_LDRSTR_Wm_SXTW_0(uint32_t operation, + uint32_t size, uint32_t Rt, + uint32_t Rn, uint32_t Rm); + + uint32_t A64_STR_IMM_PreIndex(uint32_t Rt, uint32_t Rn, int32_t simm); + uint32_t A64_LDR_IMM_PostIndex(uint32_t Rt,uint32_t Rn, int32_t simm); + + uint32_t A64_ADD_X_Wm_SXTW(uint32_t Rd, uint32_t Rn, uint32_t Rm, + uint32_t amount); + uint32_t A64_SUB_X_Wm_SXTW(uint32_t Rd, uint32_t Rn, uint32_t Rm, + uint32_t amount); + + uint32_t A64_ADD_IMM_X(uint32_t Rd, uint32_t Rn, + uint32_t imm, uint32_t shift = 0); + uint32_t A64_SUB_IMM_X(uint32_t Rd, uint32_t Rn, + uint32_t imm, uint32_t shift = 0); + + uint32_t A64_ADD_X(uint32_t Rd, uint32_t Rn, + uint32_t Rm, uint32_t shift = 0, uint32_t amount = 0); + uint32_t A64_ADD_W(uint32_t Rd, uint32_t Rn, uint32_t Rm, + uint32_t shift = 0, uint32_t amount = 0); + uint32_t A64_SUB_W(uint32_t Rd, uint32_t Rn, uint32_t Rm, + uint32_t shift = 0, uint32_t amount = 0, + uint32_t setflag = 0); + uint32_t A64_AND_W(uint32_t Rd, uint32_t Rn, + uint32_t Rm, uint32_t shift = 0, uint32_t amount = 0); + uint32_t A64_ORR_W(uint32_t Rd, uint32_t Rn, + uint32_t Rm, uint32_t shift = 0, uint32_t amount = 0); + uint32_t A64_ORN_W(uint32_t Rd, uint32_t Rn, + uint32_t Rm, uint32_t shift = 0, uint32_t amount = 0); + + uint32_t A64_MOVZ_W(uint32_t Rd, uint32_t imm, uint32_t shift); + uint32_t A64_MOVZ_X(uint32_t Rd, uint32_t imm, uint32_t shift); + uint32_t A64_MOVK_W(uint32_t Rd, uint32_t imm, uint32_t shift); + + uint32_t A64_SMADDL(uint32_t Rd, uint32_t Rn, uint32_t Rm, uint32_t Ra); + uint32_t A64_MADD_W(uint32_t Rd, uint32_t Rn, uint32_t Rm, uint32_t Ra); + + uint32_t A64_SBFM_W(uint32_t Rd, uint32_t Rn, + uint32_t immr, uint32_t imms); + uint32_t A64_UBFM_W(uint32_t Rd, uint32_t Rn, + uint32_t immr, uint32_t imms); + uint32_t A64_UBFM_X(uint32_t Rd, uint32_t Rn, + uint32_t immr, uint32_t imms); + + uint32_t A64_EXTR_W(uint32_t Rd, uint32_t Rn, uint32_t Rm, uint32_t lsb); + uint32_t A64_CSEL_X(uint32_t Rd, uint32_t Rn, uint32_t Rm, uint32_t cond); + uint32_t A64_CSEL_W(uint32_t Rd, uint32_t Rn, uint32_t Rm, uint32_t cond); + + uint32_t* mBase; + uint32_t* mPC; + uint32_t* mPrologPC; + int64_t mDuration; + uint32_t mTmpReg1, mTmpReg2, mTmpReg3, mZeroReg; + + 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; + }; + + sp<Assembly> mAssembly; + Vector<branch_target_t> mBranchTargets; + KeyedVector< const char*, uint32_t* > mLabels; + KeyedVector< uint32_t*, const char* > mLabelsInverseMapping; + KeyedVector< uint32_t*, const char* > mComments; + + enum operand_type_t + { + OPERAND_REG = 0x20, + OPERAND_IMM, + OPERAND_REG_IMM, + OPERAND_REG_OFFSET, + OPERAND_UNSUPPORTED + }; + + struct addr_mode_t { + int32_t immediate; + bool writeback; + bool preindex; + bool postindex; + int32_t reg_imm_Rm; + int32_t reg_imm_type; + uint32_t reg_imm_shift; + int32_t reg_offset; + } mAddrMode; + +}; + +}; // namespace android + +#endif //ANDROID_ARM64ASSEMBLER_H diff --git a/libpixelflinger/codeflinger/Arm64Disassembler.cpp b/libpixelflinger/codeflinger/Arm64Disassembler.cpp new file mode 100644 index 0000000..70f1ff1 --- /dev/null +++ b/libpixelflinger/codeflinger/Arm64Disassembler.cpp @@ -0,0 +1,316 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <inttypes.h> +#include <string.h> + +struct disasm_table_entry_t +{ + uint32_t mask; + uint32_t value; + const char* instr_template; +}; + + +static disasm_table_entry_t disasm_table[] = +{ + {0xff000000, 0x91000000, "add <xd|sp>, <xn|sp>, #<imm1>, <shift1>"}, + {0xff000000, 0xd1000000, "sub <xd|sp>, <xn|sp>, #<imm1>, <shift1>"}, + {0xff200000, 0x8b000000, "add <xd>, <xn>, <xm>, <shift2> #<amt1>"}, + {0xff200000, 0x0b000000, "add <wd>, <wn>, <wm>, <shift2> #<amt1>"}, + {0xff200000, 0x4b000000, "sub <wd>, <wn>, <wm>, <shift2> #<amt1>"}, + {0xff200000, 0x6b000000, "subs <wd>, <wn>, <wm>, <shift2> #<amt1>"}, + {0xff200000, 0x0a000000, "and <wd>, <wn>, <wm>, <shift2> #<amt1>"}, + {0xff200000, 0x2a000000, "orr <wd>, <wn>, <wm>, <shift2> #<amt1>"}, + {0xff200000, 0x2a200000, "orn <wd>, <wn>, <wm>, <shift2> #<amt1>"}, + {0xff800000, 0x72800000, "movk <wd>, #<imm2>, lsl #<shift3>"}, + {0xff800000, 0x52800000, "movz <wd>, #<imm2>, lsl #<shift3>"}, + {0xff800000, 0xd2800000, "movz <xd>, #<imm2>, lsl #<shift3>"}, + {0xffe00c00, 0x1a800000, "csel <wd>, <wn>, <wm>, <cond1>"}, + {0xffe00c00, 0x9a800000, "csel <xd>, <xn>, <xm>, <cond1>"}, + {0xffe00c00, 0x5a800000, "csinv <wd>, <wn>, <wm>, <cond1>"}, + {0xffe08000, 0x1b000000, "madd <wd>, <wn>, <wm>, <wa>"}, + {0xffe08000, 0x9b200000, "smaddl <xd>, <wn>, <wm>, <xa>"}, + {0xffe04c00, 0xb8604800, "ldr <wt>, [<xn|sp>, <r1><m1>, <ext1> #<amt2>]"}, + {0xffe04c00, 0xb8204800, "str <wt>, [<xn|sp>, <r1><m1>, <ext1> #<amt2>]"}, + {0xffe04c00, 0xf8604800, "ldr <xt>, [<xn|sp>, <r1><m1>, <ext1> #<amt3>]"}, + {0xffe04c00, 0xf8204800, "str <xt>, [<xn|sp>, <r1><m1>, <ext1> #<amt3>]"}, + {0xffe04c00, 0x38604800, "ldrb <wt>, [<xn|sp>, <r1><m1>, <ext1> <amt5>]"}, + {0xffe04c00, 0x38204800, "strb <wt>, [<xn|sp>, <r1><m1>, <ext1> <amt5>]"}, + {0xffe04c00, 0x78604800, "ldrh <wt>, [<xn|sp>, <r1><m1>, <ext1> #<amt6>]"}, + {0xffe04c00, 0x78204800, "strh <wt>, [<xn|sp>, <r1><m1>, <ext1> #<amt6>]"}, + {0xffe00c00, 0xb8400400, "ldr <wt>, [<xn|sp>], #<simm1>"}, + {0xffe00c00, 0xb8000c00, "str <wt>, [<xn|sp>, #<simm1>]!"}, + {0xffc00000, 0x13000000, "sbfm <wd>, <wn>, #<immr1>, #<imms1>"}, + {0xffc00000, 0x53000000, "ubfm <wd>, <wn>, #<immr1>, #<imms1>"}, + {0xffc00000, 0xd3400000, "ubfm <xd>, <xn>, #<immr1>, #<imms1>"}, + {0xffe00000, 0x13800000, "extr <wd>, <wn>, <wm>, #<lsb1>"}, + {0xff000000, 0x54000000, "b.<cond2> <label1>"}, + {0xfffffc1f, 0xd65f0000, "ret <xn>"}, + {0xffe00000, 0x8b200000, "add <xd|sp>, <xn|sp>, <r2><m1>, <ext2> #<amt4>"}, + {0xffe00000, 0xcb200000, "sub <xd|sp>, <xn|sp>, <r2><m1>, <ext2> #<amt4>"} +}; + +static int32_t bits_signed(uint32_t instr, uint32_t msb, uint32_t lsb) +{ + int32_t value; + value = ((int32_t)instr) << (31 - msb); + value >>= (31 - msb); + value >>= lsb; + return value; +} +static uint32_t bits_unsigned(uint32_t instr, uint32_t msb, uint32_t lsb) +{ + uint32_t width = msb - lsb + 1; + uint32_t mask = (1 << width) - 1; + return ((instr >> lsb) & mask); +} + +static void get_token(const char *instr, uint32_t index, char *token) +{ + uint32_t i, j; + for(i = index, j = 0; i < strlen(instr); ++i) + { + if(instr[index] == '<' && instr[i] == '>') + { + token[j++] = instr[i]; + break; + } + else if(instr[index] != '<' && instr[i] == '<') + { + break; + } + else + { + token[j++] = instr[i]; + } + } + token[j] = '\0'; + return; +} + + +static const char * token_cc_table[] = +{ + "eq", "ne", "cs", "cc", "mi", + "pl", "vs", "vc", "hi", "ls", + "ge", "lt", "gt", "le", "al", "nv" +}; + +static void decode_rx_zr_token(uint32_t reg, const char *prefix, char *instr_part) +{ + if(reg == 31) + sprintf(instr_part, "%s%s", prefix, "zr"); + else + sprintf(instr_part, "%s%d", prefix, reg); +} + +static void decode_token(uint32_t code, char *token, char *instr_part) +{ + if(strcmp(token, "<imm1>") == 0) + sprintf(instr_part, "0x%x", bits_unsigned(code, 21,10)); + else if(strcmp(token, "<imm2>") == 0) + sprintf(instr_part, "0x%x", bits_unsigned(code, 20,5)); + else if(strcmp(token, "<shift1>") == 0) + sprintf(instr_part, "lsl #%d", bits_unsigned(code, 23,22) * 12); + else if(strcmp(token, "<shift2>") == 0) + { + static const char * shift2_table[] = { "lsl", "lsr", "asr", "ror"}; + sprintf(instr_part, "%s", shift2_table[bits_unsigned(code, 23,22)]); + } + else if(strcmp(token, "<shift3>") == 0) + sprintf(instr_part, "%d", bits_unsigned(code, 22,21) * 16); + else if(strcmp(token, "<amt1>") == 0) + sprintf(instr_part, "%d", bits_unsigned(code, 15,10)); + else if(strcmp(token, "<amt2>") == 0) + sprintf(instr_part, "%d", bits_unsigned(code, 12,12) * 2); + else if(strcmp(token, "<amt3>") == 0) + sprintf(instr_part, "%d", bits_unsigned(code, 12,12) * 3); + else if(strcmp(token, "<amt4>") == 0) + sprintf(instr_part, "%d", bits_unsigned(code, 12,10)); + else if(strcmp(token, "<amt5>") == 0) + { + static const char * amt5_table[] = {"", "#0"}; + sprintf(instr_part, "%s", amt5_table[bits_unsigned(code, 12,12)]); + } + else if(strcmp(token, "<amt6>") == 0) + sprintf(instr_part, "%d", bits_unsigned(code, 12,12)); + else if(strcmp(token, "<simm1>") == 0) + sprintf(instr_part, "%d", bits_signed(code, 20,12)); + else if(strcmp(token, "<immr1>") == 0) + sprintf(instr_part, "%d", bits_unsigned(code, 21,16)); + else if(strcmp(token, "<imms1>") == 0) + sprintf(instr_part, "%d", bits_unsigned(code, 15,10)); + else if(strcmp(token, "<lsb1>") == 0) + sprintf(instr_part, "%d", bits_unsigned(code, 15,10)); + else if(strcmp(token, "<cond1>") == 0) + sprintf(instr_part, "%s", token_cc_table[bits_unsigned(code, 15,12)]); + else if(strcmp(token, "<cond2>") == 0) + sprintf(instr_part, "%s", token_cc_table[bits_unsigned(code, 4,0)]); + else if(strcmp(token, "<r1>") == 0) + { + const char * token_r1_table[] = + { + "reserved", "reserved", "w", "x", + "reserved", "reserved", "w", "x" + }; + sprintf(instr_part, "%s", token_r1_table[bits_unsigned(code, 15,13)]); + } + else if(strcmp(token, "<r2>") == 0) + { + static const char * token_r2_table[] = + { + "w","w","w", "x", "w", "w", "w", "x" + }; + sprintf(instr_part, "%s", token_r2_table[bits_unsigned(code, 15,13)]); + } + else if(strcmp(token, "<m1>") == 0) + { + uint32_t reg = bits_unsigned(code, 20,16); + if(reg == 31) + sprintf(instr_part, "%s", "zr"); + else + sprintf(instr_part, "%d", reg); + } + else if(strcmp(token, "<ext1>") == 0) + { + static const char * token_ext1_table[] = + { + "reserved","reserved","uxtw", "lsl", + "reserved","reserved", "sxtw", "sxtx" + }; + sprintf(instr_part, "%s", token_ext1_table[bits_unsigned(code, 15,13)]); + } + else if(strcmp(token, "<ext2>") == 0) + { + static const char * token_ext2_table[] = + { + "uxtb","uxth","uxtw","uxtx", + "sxtb","sxth","sxtw","sxtx" + }; + sprintf(instr_part, "%s", token_ext2_table[bits_unsigned(code, 15,13)]); + } + else if (strcmp(token, "<label1>") == 0) + { + int32_t offset = bits_signed(code, 23,5) * 4; + if(offset > 0) + sprintf(instr_part, "#.+%d", offset); + else + sprintf(instr_part, "#.-%d", -offset); + } + else if (strcmp(token, "<xn|sp>") == 0) + { + uint32_t reg = bits_unsigned(code, 9, 5); + if(reg == 31) + sprintf(instr_part, "%s", "sp"); + else + sprintf(instr_part, "x%d", reg); + } + else if (strcmp(token, "<xd|sp>") == 0) + { + uint32_t reg = bits_unsigned(code, 4, 0); + if(reg == 31) + sprintf(instr_part, "%s", "sp"); + else + sprintf(instr_part, "x%d", reg); + } + else if (strcmp(token, "<xn>") == 0) + decode_rx_zr_token(bits_unsigned(code, 9, 5), "x", instr_part); + else if (strcmp(token, "<xd>") == 0) + decode_rx_zr_token(bits_unsigned(code, 4, 0), "x", instr_part); + else if (strcmp(token, "<xm>") == 0) + decode_rx_zr_token(bits_unsigned(code, 20, 16), "x", instr_part); + else if (strcmp(token, "<xa>") == 0) + decode_rx_zr_token(bits_unsigned(code, 14, 10), "x", instr_part); + else if (strcmp(token, "<xt>") == 0) + decode_rx_zr_token(bits_unsigned(code, 4, 0), "x", instr_part); + else if (strcmp(token, "<wn>") == 0) + decode_rx_zr_token(bits_unsigned(code, 9, 5), "w", instr_part); + else if (strcmp(token, "<wd>") == 0) + decode_rx_zr_token(bits_unsigned(code, 4, 0), "w", instr_part); + else if (strcmp(token, "<wm>") == 0) + decode_rx_zr_token(bits_unsigned(code, 20, 16), "w", instr_part); + else if (strcmp(token, "<wa>") == 0) + decode_rx_zr_token(bits_unsigned(code, 14, 10), "w", instr_part); + else if (strcmp(token, "<wt>") == 0) + decode_rx_zr_token(bits_unsigned(code, 4, 0), "w", instr_part); + else + { + sprintf(instr_part, "error"); + } + return; +} + +int arm64_disassemble(uint32_t code, char* instr) +{ + uint32_t i; + char token[256]; + char instr_part[256]; + + if(instr == NULL) + return -1; + + bool matched = false; + disasm_table_entry_t *entry = NULL; + for(i = 0; i < sizeof(disasm_table)/sizeof(disasm_table_entry_t); ++i) + { + entry = &disasm_table[i]; + if((code & entry->mask) == entry->value) + { + matched = true; + break; + } + } + if(matched == false) + { + strcpy(instr, "Unknown Instruction"); + return -1; + } + else + { + uint32_t index = 0; + uint32_t length = strlen(entry->instr_template); + instr[0] = '\0'; + do + { + get_token(entry->instr_template, index, token); + if(token[0] == '<') + { + decode_token(code, token, instr_part); + strcat(instr, instr_part); + } + else + { + strcat(instr, token); + } + index += strlen(token); + }while(index < length); + return 0; + } +} diff --git a/libpixelflinger/codeflinger/Arm64Disassembler.h b/libpixelflinger/codeflinger/Arm64Disassembler.h new file mode 100644 index 0000000..86f3aba --- /dev/null +++ b/libpixelflinger/codeflinger/Arm64Disassembler.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef ANDROID_ARM64DISASSEMBLER_H +#define ANDROID_ARM64DISASSEMBLER_H + +#include <inttypes.h> +int arm64_disassemble(uint32_t code, char* instr); + +#endif //ANDROID_ARM64ASSEMBLER_H diff --git a/libpixelflinger/codeflinger/CodeCache.cpp b/libpixelflinger/codeflinger/CodeCache.cpp index 58fde7e..4fe30d9 100644 --- a/libpixelflinger/codeflinger/CodeCache.cpp +++ b/libpixelflinger/codeflinger/CodeCache.cpp @@ -34,7 +34,7 @@ namespace android { // ---------------------------------------------------------------------------- -#if defined(__arm__) +#if defined(__arm__) || defined(__aarch64__) #include <unistd.h> #include <errno.h> #endif @@ -201,7 +201,7 @@ int CodeCache::cache( const AssemblyKeyBase& keyBase, mCacheInUse += assemblySize; mWhen++; // synchronize caches... -#if defined(__arm__) || defined(__mips__) +#if defined(__arm__) || defined(__mips__) || defined(__aarch64__) const long base = long(assembly->base()); const long curr = base + long(assembly->size()); err = cacheflush(base, curr, 0); diff --git a/libpixelflinger/codeflinger/GGLAssembler.cpp b/libpixelflinger/codeflinger/GGLAssembler.cpp index 0cb042e..2422d7b 100644 --- a/libpixelflinger/codeflinger/GGLAssembler.cpp +++ b/libpixelflinger/codeflinger/GGLAssembler.cpp @@ -263,7 +263,7 @@ int GGLAssembler::scanline_core(const needs_t& needs, context_t const* c) const int mask = GGL_DITHER_SIZE-1; parts.dither = reg_t(regs.obtain()); AND(AL, 0, parts.dither.reg, parts.count.reg, imm(mask)); - ADD(AL, 0, parts.dither.reg, parts.dither.reg, ctxtReg); + ADDR_ADD(AL, 0, parts.dither.reg, ctxtReg, parts.dither.reg); LDRB(AL, parts.dither.reg, parts.dither.reg, immed12_pre(GGL_OFFSETOF(ditherMatrix))); } @@ -336,7 +336,7 @@ int GGLAssembler::scanline_core(const needs_t& needs, context_t const* c) build_iterate_z(parts); build_iterate_f(parts); if (!mAllMasked) { - ADD(AL, 0, parts.cbPtr.reg, parts.cbPtr.reg, imm(parts.cbPtr.size>>3)); + ADDR_ADD(AL, 0, parts.cbPtr.reg, parts.cbPtr.reg, imm(parts.cbPtr.size>>3)); } SUB(AL, S, parts.count.reg, parts.count.reg, imm(1<<16)); B(PL, "fragment_loop"); @@ -392,7 +392,7 @@ void GGLAssembler::build_scanline_prolog( int Rs = scratches.obtain(); parts.cbPtr.setTo(obtainReg(), cb_bits); CONTEXT_LOAD(Rs, state.buffers.color.stride); - CONTEXT_LOAD(parts.cbPtr.reg, state.buffers.color.data); + CONTEXT_ADDR_LOAD(parts.cbPtr.reg, state.buffers.color.data); SMLABB(AL, Rs, Ry, Rs, Rx); // Rs = Rx + Ry*Rs base_offset(parts.cbPtr, parts.cbPtr, Rs); scratches.recycle(Rs); @@ -428,11 +428,11 @@ void GGLAssembler::build_scanline_prolog( int Rs = dzdx; int zbase = scratches.obtain(); CONTEXT_LOAD(Rs, state.buffers.depth.stride); - CONTEXT_LOAD(zbase, state.buffers.depth.data); + CONTEXT_ADDR_LOAD(zbase, state.buffers.depth.data); SMLABB(AL, Rs, Ry, Rs, Rx); ADD(AL, 0, Rs, Rs, reg_imm(parts.count.reg, LSR, 16)); - ADD(AL, 0, zbase, zbase, reg_imm(Rs, LSL, 1)); - CONTEXT_STORE(zbase, generated_vars.zbase); + ADDR_ADD(AL, 0, zbase, zbase, reg_imm(Rs, LSL, 1)); + CONTEXT_ADDR_STORE(zbase, generated_vars.zbase); } // init texture coordinates @@ -445,8 +445,8 @@ void GGLAssembler::build_scanline_prolog( // init coverage factor application (anti-aliasing) if (mAA) { parts.covPtr.setTo(obtainReg(), 16); - CONTEXT_LOAD(parts.covPtr.reg, state.buffers.coverage); - ADD(AL, 0, parts.covPtr.reg, parts.covPtr.reg, reg_imm(Rx, LSL, 1)); + CONTEXT_ADDR_LOAD(parts.covPtr.reg, state.buffers.coverage); + ADDR_ADD(AL, 0, parts.covPtr.reg, parts.covPtr.reg, reg_imm(Rx, LSL, 1)); } } @@ -765,8 +765,8 @@ void GGLAssembler::build_depth_test( int depth = scratches.obtain(); int z = parts.z.reg; - CONTEXT_LOAD(zbase, generated_vars.zbase); // stall - SUB(AL, 0, zbase, zbase, reg_imm(parts.count.reg, LSR, 15)); + CONTEXT_ADDR_LOAD(zbase, generated_vars.zbase); // stall + ADDR_SUB(AL, 0, zbase, zbase, reg_imm(parts.count.reg, LSR, 15)); // above does zbase = zbase + ((count >> 16) << 1) if (mask & Z_TEST) { @@ -901,6 +901,10 @@ void GGLAssembler::build_and_immediate(int d, int s, uint32_t mask, int bits) AND( AL, 0, d, s, imm(mask) ); return; } + else if (getCodegenArch() == CODEGEN_ARCH_ARM64) { + AND( AL, 0, d, s, imm(mask) ); + return; + } int negative_logic = !isValidImmediate(mask); if (negative_logic) { @@ -990,22 +994,22 @@ void GGLAssembler::base_offset( { switch (b.size) { case 32: - ADD(AL, 0, d.reg, b.reg, reg_imm(o.reg, LSL, 2)); + ADDR_ADD(AL, 0, d.reg, b.reg, reg_imm(o.reg, LSL, 2)); break; case 24: if (d.reg == b.reg) { - ADD(AL, 0, d.reg, b.reg, reg_imm(o.reg, LSL, 1)); - ADD(AL, 0, d.reg, d.reg, o.reg); + ADDR_ADD(AL, 0, d.reg, b.reg, reg_imm(o.reg, LSL, 1)); + ADDR_ADD(AL, 0, d.reg, d.reg, o.reg); } else { - ADD(AL, 0, d.reg, o.reg, reg_imm(o.reg, LSL, 1)); - ADD(AL, 0, d.reg, d.reg, b.reg); + ADDR_ADD(AL, 0, d.reg, o.reg, reg_imm(o.reg, LSL, 1)); + ADDR_ADD(AL, 0, d.reg, d.reg, b.reg); } break; case 16: - ADD(AL, 0, d.reg, b.reg, reg_imm(o.reg, LSL, 1)); + ADDR_ADD(AL, 0, d.reg, b.reg, reg_imm(o.reg, LSL, 1)); break; case 8: - ADD(AL, 0, d.reg, b.reg, o.reg); + ADDR_ADD(AL, 0, d.reg, b.reg, o.reg); break; } } diff --git a/libpixelflinger/codeflinger/GGLAssembler.h b/libpixelflinger/codeflinger/GGLAssembler.h index d993684..9db20df 100644 --- a/libpixelflinger/codeflinger/GGLAssembler.h +++ b/libpixelflinger/codeflinger/GGLAssembler.h @@ -31,6 +31,12 @@ namespace android { // ---------------------------------------------------------------------------- +#define CONTEXT_ADDR_LOAD(REG, FIELD) \ + ADDR_LDR(AL, REG, mBuilderContext.Rctx, immed12_pre(GGL_OFFSETOF(FIELD))) + +#define CONTEXT_ADDR_STORE(REG, FIELD) \ + ADDR_STR(AL, REG, mBuilderContext.Rctx, immed12_pre(GGL_OFFSETOF(FIELD))) + #define CONTEXT_LOAD(REG, FIELD) \ LDR(AL, REG, mBuilderContext.Rctx, immed12_pre(GGL_OFFSETOF(FIELD))) diff --git a/libpixelflinger/codeflinger/texturing.cpp b/libpixelflinger/codeflinger/texturing.cpp index 9e3d217..b2cfbb3 100644 --- a/libpixelflinger/codeflinger/texturing.cpp +++ b/libpixelflinger/codeflinger/texturing.cpp @@ -356,7 +356,7 @@ void GGLAssembler::init_textures( // merge base & offset CONTEXT_LOAD(txPtr.reg, generated_vars.texture[i].stride); SMLABB(AL, Rx, Ry, txPtr.reg, Rx); // x+y*stride - CONTEXT_LOAD(txPtr.reg, generated_vars.texture[i].data); + CONTEXT_ADDR_LOAD(txPtr.reg, generated_vars.texture[i].data); base_offset(txPtr, txPtr, Rx); } else { Scratch scratches(registerFile()); @@ -629,7 +629,7 @@ void GGLAssembler::build_textures( fragment_parts_t& parts, return; CONTEXT_LOAD(stride, generated_vars.texture[i].stride); - CONTEXT_LOAD(txPtr.reg, generated_vars.texture[i].data); + CONTEXT_ADDR_LOAD(txPtr.reg, generated_vars.texture[i].data); SMLABB(AL, u, v, stride, u); // u+v*stride base_offset(txPtr, txPtr, u); diff --git a/libpixelflinger/scanline.cpp b/libpixelflinger/scanline.cpp index a5d28b2..aa23ca6 100644 --- a/libpixelflinger/scanline.cpp +++ b/libpixelflinger/scanline.cpp @@ -31,8 +31,11 @@ #include "codeflinger/CodeCache.h" #include "codeflinger/GGLAssembler.h" +#if defined(__arm__) #include "codeflinger/ARMAssembler.h" -#if defined(__mips__) +#elif defined(__aarch64__) +#include "codeflinger/Arm64Assembler.h" +#elif defined(__mips__) #include "codeflinger/MIPSAssembler.h" #endif //#include "codeflinger/ARMAssemblerOptimizer.h" @@ -52,7 +55,7 @@ # define ANDROID_CODEGEN ANDROID_CODEGEN_GENERATED #endif -#if defined(__arm__) || defined(__mips__) +#if defined(__arm__) || defined(__mips__) || defined(__aarch64__) # define ANDROID_ARM_CODEGEN 1 #else # define ANDROID_ARM_CODEGEN 0 @@ -68,6 +71,8 @@ #ifdef __mips__ #define ASSEMBLY_SCRATCH_SIZE 4096 +#elif defined(__aarch64__) +#define ASSEMBLY_SCRATCH_SIZE 8192 #else #define ASSEMBLY_SCRATCH_SIZE 2048 #endif @@ -122,6 +127,9 @@ extern "C" void scanline_t32cb16blend_arm(uint16_t*, uint32_t*, size_t); extern "C" void scanline_t32cb16_arm(uint16_t *dst, uint32_t *src, size_t ct); extern "C" void scanline_col32cb16blend_neon(uint16_t *dst, uint32_t *col, size_t ct); extern "C" void scanline_col32cb16blend_arm(uint16_t *dst, uint32_t col, size_t ct); +#elif defined(__aarch64__) +extern "C" void scanline_t32cb16blend_arm64(uint16_t*, uint32_t*, size_t); +extern "C" void scanline_col32cb16blend_arm64(uint16_t *dst, uint32_t col, size_t ct); #elif defined(__mips__) extern "C" void scanline_t32cb16blend_mips(uint16_t*, uint32_t*, size_t); #endif @@ -276,6 +284,8 @@ static const needs_filter_t fill16noblend = { #if defined(__mips__) static CodeCache gCodeCache(32 * 1024); +#elif defined(__aarch64__) +static CodeCache gCodeCache(48 * 1024); #else static CodeCache gCodeCache(12 * 1024); #endif @@ -394,6 +404,8 @@ static void pick_scanline(context_t* c) #endif #if defined(__mips__) GGLAssembler assembler( new ArmToMipsAssembler(a) ); +#elif defined(__aarch64__) + GGLAssembler assembler( new ArmToArm64Assembler(a) ); #endif // generate the scanline code for the given needs int err = assembler.scanline(c->state.needs, c); @@ -1717,7 +1729,7 @@ void init_y(context_t* c, int32_t ys) gen.width = t.surface.width; gen.height = t.surface.height; gen.stride = t.surface.stride; - gen.data = int32_t(t.surface.data); + gen.data = uintptr_t(t.surface.data); gen.dsdx = ti.dsdx; gen.dtdx = ti.dtdx; } @@ -1877,7 +1889,7 @@ void scanline_perspective(context_t* c) struct { int32_t s, sq; int32_t t, tq; - }; + } sqtq; struct { int32_t v, q; } st[2]; @@ -1916,10 +1928,10 @@ void scanline_perspective(context_t* c) int32_t t = tmu.shade.it0 + (tmu.shade.idtdy * ys) + (tmu.shade.idtdx * xs) + ((tmu.shade.idtdx + tmu.shade.idtdy)>>1); - tc[i].s = s; - tc[i].t = t; - tc[i].sq = gglMulx(s, q0, iwscale); - tc[i].tq = gglMulx(t, q0, iwscale); + tc[i].sqtq.s = s; + tc[i].sqtq.t = t; + tc[i].sqtq.sq = gglMulx(s, q0, iwscale); + tc[i].sqtq.tq = gglMulx(t, q0, iwscale); } int32_t span = 0; @@ -2085,6 +2097,8 @@ void scanline_col32cb16blend(context_t* c) #else // defined(__ARM_HAVE_NEON) && BYTE_ORDER == LITTLE_ENDIAN scanline_col32cb16blend_arm(dst, GGL_RGBA_TO_HOST(c->packed8888), ct); #endif // defined(__ARM_HAVE_NEON) && BYTE_ORDER == LITTLE_ENDIAN +#elif ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && defined(__aarch64__)) + scanline_col32cb16blend_arm64(dst, GGL_RGBA_TO_HOST(c->packed8888), ct); #else uint32_t s = GGL_RGBA_TO_HOST(c->packed8888); int sA = (s>>24); @@ -2125,7 +2139,7 @@ void scanline_t32cb16(context_t* c) int sR, sG, sB; uint32_t s, d; - if (ct==1 || uint32_t(dst)&2) { + if (ct==1 || uintptr_t(dst)&2) { last_one: s = GGL_RGBA_TO_HOST( *src++ ); *dst++ = convertAbgr8888ToRgb565(s); @@ -2157,7 +2171,7 @@ last_one: void scanline_t32cb16blend(context_t* c) { -#if ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && (defined(__arm__) || defined(__mips))) +#if ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && (defined(__arm__) || defined(__mips__) || defined(__aarch64__))) int32_t x = c->iterators.xl; size_t ct = c->iterators.xr - x; int32_t y = c->iterators.y; @@ -2171,7 +2185,9 @@ void scanline_t32cb16blend(context_t* c) #ifdef __arm__ scanline_t32cb16blend_arm(dst, src, ct); -#else +#elif defined(__aarch64__) + scanline_t32cb16blend_arm64(dst, src, ct); +#elif defined(__mips__) scanline_t32cb16blend_mips(dst, src, ct); #endif #else diff --git a/libpixelflinger/tests/arch-arm64/Android.mk b/libpixelflinger/tests/arch-arm64/Android.mk new file mode 100644 index 0000000..ca58b4b --- /dev/null +++ b/libpixelflinger/tests/arch-arm64/Android.mk @@ -0,0 +1,3 @@ +ifeq ($(TARGET_ARCH),arm64) +include $(all-subdir-makefiles) +endif diff --git a/libpixelflinger/tests/arch-arm64/assembler/Android.mk b/libpixelflinger/tests/arch-arm64/assembler/Android.mk new file mode 100644 index 0000000..36db49c --- /dev/null +++ b/libpixelflinger/tests/arch-arm64/assembler/Android.mk @@ -0,0 +1,19 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + arm64_assembler_test.cpp\ + asm_test_jacket.S + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libpixelflinger + +LOCAL_C_INCLUDES := \ + system/core/libpixelflinger + +LOCAL_MODULE:= test-pixelflinger-arm64-assembler-test + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) diff --git a/libpixelflinger/tests/arch-arm64/assembler/arm64_assembler_test.cpp b/libpixelflinger/tests/arch-arm64/assembler/arm64_assembler_test.cpp new file mode 100644 index 0000000..84381d5 --- /dev/null +++ b/libpixelflinger/tests/arch-arm64/assembler/arm64_assembler_test.cpp @@ -0,0 +1,782 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> + +#include <sys/mman.h> +#include <cutils/ashmem.h> +#include <cutils/atomic.h> + +#define __STDC_FORMAT_MACROS +#include <inttypes.h> + +#include "codeflinger/ARMAssemblerInterface.h" +#include "codeflinger/Arm64Assembler.h" +using namespace android; + +#define TESTS_DATAOP_ENABLE 1 +#define TESTS_DATATRANSFER_ENABLE 1 +#define TESTS_LDMSTM_ENABLE 1 +#define TESTS_REG_CORRUPTION_ENABLE 0 + +void *instrMem; +uint32_t instrMemSize = 128 * 1024; +char dataMem[8192]; + +typedef void (*asm_function_t)(); +extern "C" void asm_test_jacket(asm_function_t function, + int64_t regs[], int32_t flags[]); + +#define MAX_32BIT (uint32_t)(((uint64_t)1 << 32) - 1) +const uint32_t NA = 0; +const uint32_t NUM_REGS = 32; +const uint32_t NUM_FLAGS = 16; + +enum instr_t +{ + INSTR_ADD, + INSTR_SUB, + INSTR_AND, + INSTR_ORR, + INSTR_RSB, + INSTR_BIC, + INSTR_CMP, + INSTR_MOV, + INSTR_MVN, + INSTR_MUL, + INSTR_MLA, + INSTR_SMULBB, + INSTR_SMULBT, + INSTR_SMULTB, + INSTR_SMULTT, + INSTR_SMULWB, + INSTR_SMULWT, + INSTR_SMLABB, + INSTR_UXTB16, + INSTR_UBFX, + INSTR_ADDR_ADD, + INSTR_ADDR_SUB, + INSTR_LDR, + INSTR_LDRB, + INSTR_LDRH, + INSTR_ADDR_LDR, + INSTR_LDM, + INSTR_STR, + INSTR_STRB, + INSTR_STRH, + INSTR_ADDR_STR, + INSTR_STM +}; + +enum shift_t +{ + SHIFT_LSL, + SHIFT_LSR, + SHIFT_ASR, + SHIFT_ROR, + SHIFT_NONE +}; + +enum offset_t +{ + REG_SCALE_OFFSET, + REG_OFFSET, + IMM8_OFFSET, + IMM12_OFFSET, + NO_OFFSET +}; + +enum cond_t +{ + EQ, NE, CS, CC, MI, PL, VS, VC, HI, LS, GE, LT, GT, LE, AL, NV, + HS = CS, + LO = CC +}; + +const char * cc_code[] = +{ + "EQ", "NE", "CS", "CC", "MI", "PL", "VS", "VC", + "HI", "LS","GE","LT", "GT", "LE", "AL", "NV" +}; + + +struct dataOpTest_t +{ + uint32_t id; + instr_t op; + uint32_t preFlag; + cond_t cond; + bool setFlags; + uint64_t RnValue; + uint64_t RsValue; + bool immediate; + uint32_t immValue; + uint64_t RmValue; + uint32_t shiftMode; + uint32_t shiftAmount; + uint64_t RdValue; + bool checkRd; + uint64_t postRdValue; + bool checkFlag; + uint32_t postFlag; +}; + +struct dataTransferTest_t +{ + uint32_t id; + instr_t op; + uint32_t preFlag; + cond_t cond; + bool setMem; + uint64_t memOffset; + uint64_t memValue; + uint64_t RnValue; + offset_t offsetType; + uint64_t RmValue; + uint32_t immValue; + bool writeBack; + bool preIndex; + bool postIndex; + uint64_t RdValue; + uint64_t postRdValue; + uint64_t postRnValue; + bool checkMem; + uint64_t postMemOffset; + uint32_t postMemLength; + uint64_t postMemValue; +}; + + +dataOpTest_t dataOpTests [] = +{ + {0xA000,INSTR_ADD,AL,AL,0,1,NA,1,MAX_32BIT ,NA,NA,NA,NA,1,0,0,0}, + {0xA001,INSTR_ADD,AL,AL,0,1,NA,1,MAX_32BIT -1,NA,NA,NA,NA,1,MAX_32BIT,0,0}, + {0xA002,INSTR_ADD,AL,AL,0,1,NA,0,NA,MAX_32BIT ,NA,NA,NA,1,0,0,0}, + {0xA003,INSTR_ADD,AL,AL,0,1,NA,0,NA,MAX_32BIT -1,NA,NA,NA,1,MAX_32BIT,0,0}, + {0xA004,INSTR_ADD,AL,AL,0,1,NA,0,0,MAX_32BIT ,SHIFT_LSL,0,NA,1,0,0,0}, + {0xA005,INSTR_ADD,AL,AL,0,1,NA,0,0,MAX_32BIT ,SHIFT_LSL,31,NA,1,0x80000001,0,0}, + {0xA006,INSTR_ADD,AL,AL,0,1,NA,0,0,3,SHIFT_LSR,1,NA,1,2,0,0}, + {0xA007,INSTR_ADD,AL,AL,0,1,NA,0,0,MAX_32BIT ,SHIFT_LSR,31,NA,1,2,0,0}, + {0xA008,INSTR_ADD,AL,AL,0,0,NA,0,0,3,SHIFT_ASR,1,NA,1,1,0,0}, + {0xA009,INSTR_ADD,AL,AL,0,1,NA,0,0,MAX_32BIT ,SHIFT_ASR,31,NA,1,0,0,0}, + {0xA010,INSTR_AND,AL,AL,0,1,NA,1,MAX_32BIT ,0,0,0,NA,1,1,0,0}, + {0xA011,INSTR_AND,AL,AL,0,1,NA,1,MAX_32BIT -1,0,0,0,NA,1,0,0,0}, + {0xA012,INSTR_AND,AL,AL,0,1,NA,0,0,MAX_32BIT ,0,0,NA,1,1,0,0}, + {0xA013,INSTR_AND,AL,AL,0,1,NA,0,0,MAX_32BIT -1,0,0,NA,1,0,0,0}, + {0xA014,INSTR_AND,AL,AL,0,1,NA,0,0,MAX_32BIT ,SHIFT_LSL,0,NA,1,1,0,0}, + {0xA015,INSTR_AND,AL,AL,0,1,NA,0,0,MAX_32BIT ,SHIFT_LSL,31,NA,1,0,0,0}, + {0xA016,INSTR_AND,AL,AL,0,1,NA,0,0,3,SHIFT_LSR,1,NA,1,1,0,0}, + {0xA017,INSTR_AND,AL,AL,0,1,NA,0,0,MAX_32BIT ,SHIFT_LSR,31,NA,1,1,0,0}, + {0xA018,INSTR_AND,AL,AL,0,0,NA,0,0,3,SHIFT_ASR,1,NA,1,0,0,0}, + {0xA019,INSTR_AND,AL,AL,0,1,NA,0,0,MAX_32BIT ,SHIFT_ASR,31,NA,1,1,0,0}, + {0xA020,INSTR_ORR,AL,AL,0,3,NA,1,MAX_32BIT ,0,0,0,NA,1,MAX_32BIT,0,0}, + {0xA021,INSTR_ORR,AL,AL,0,2,NA,1,MAX_32BIT -1,0,0,0,NA,1,MAX_32BIT-1,0,0}, + {0xA022,INSTR_ORR,AL,AL,0,3,NA,0,0,MAX_32BIT ,0,0,NA,1,MAX_32BIT,0,0}, + {0xA023,INSTR_ORR,AL,AL,0,2,NA,0,0,MAX_32BIT -1,0,0,NA,1,MAX_32BIT-1,0,0}, + {0xA024,INSTR_ORR,AL,AL,0,1,NA,0,0,MAX_32BIT ,SHIFT_LSL,0,NA,1,MAX_32BIT,0,0}, + {0xA025,INSTR_ORR,AL,AL,0,1,NA,0,0,MAX_32BIT ,SHIFT_LSL,31,NA,1,0x80000001,0,0}, + {0xA026,INSTR_ORR,AL,AL,0,1,NA,0,0,3,SHIFT_LSR,1,NA,1,1,0,0}, + {0xA027,INSTR_ORR,AL,AL,0,0,NA,0,0,MAX_32BIT ,SHIFT_LSR,31,NA,1,1,0,0}, + {0xA028,INSTR_ORR,AL,AL,0,0,NA,0,0,3,SHIFT_ASR,1,NA,1,1,0,0}, + {0xA029,INSTR_ORR,AL,AL,0,1,NA,0,0,MAX_32BIT ,SHIFT_ASR,31,NA,1,MAX_32BIT ,0,0}, + {0xA030,INSTR_CMP,AL,AL,1,0x10000,NA,1,0x10000,0,0,0,NA,0,0,1,HS}, + {0xA031,INSTR_CMP,AL,AL,1,0x00000,NA,1,0x10000,0,0,0,NA,0,0,1,CC}, + {0xA032,INSTR_CMP,AL,AL,1,0x00000,NA,0,0,0x10000,0,0,NA,0,0,1,LT}, + {0xA033,INSTR_CMP,AL,AL,1,0x10000,NA,0,0,0x10000,0,0,NA,0,0,1,EQ}, + {0xA034,INSTR_CMP,AL,AL,1,0x00000,NA,0,0,0x10000,0,0,NA,0,0,1,LS}, + {0xA035,INSTR_CMP,AL,AL,1,0x10000,NA,0,0,0x10000,0,0,NA,0,0,1,LS}, + {0xA036,INSTR_CMP,AL,AL,1,0x10000,NA,0,0,0x00000,0,0,NA,0,0,1,HI}, + {0xA037,INSTR_CMP,AL,AL,1,0x10000,NA,0,0,0x10000,0,0,NA,0,0,1,HS}, + {0xA038,INSTR_CMP,AL,AL,1,0x10000,NA,0,0,0x00000,0,0,NA,0,0,1,HS}, + {0xA039,INSTR_CMP,AL,AL,1,0x10000,NA,0,0,0x00000,0,0,NA,0,0,1,NE}, + {0xA040,INSTR_CMP,AL,AL,1,0,NA,0,0,MAX_32BIT ,SHIFT_LSR,1,NA,0,0,1,LT}, + {0xA041,INSTR_CMP,AL,AL,1,1,NA,0,0,MAX_32BIT ,SHIFT_LSR,31,NA,0,0,1,EQ}, + {0xA042,INSTR_CMP,AL,AL,1,0,NA,0,0,0x10000,SHIFT_LSR,31,NA,0,0,1,LS}, + {0xA043,INSTR_CMP,AL,AL,1,0x10000,NA,0,0,0x30000,SHIFT_LSR,1,NA,0,0,1,LS}, + {0xA044,INSTR_CMP,AL,AL,1,0x10000,NA,0,0,0x00000,SHIFT_LSR,31,NA,0,0,1,HI}, + {0xA045,INSTR_CMP,AL,AL,1,1,NA,0,0,MAX_32BIT ,SHIFT_LSR,31,NA,0,0,1,HS}, + {0xA046,INSTR_CMP,AL,AL,1,0x10000,NA,0,0,0x2000,SHIFT_LSR,1,NA,0,0,1,HS}, + {0xA047,INSTR_CMP,AL,AL,1,0,NA,0,0,MAX_32BIT ,SHIFT_LSR,1,NA,0,0,1,NE}, + {0xA048,INSTR_CMP,AL,AL,1,0,NA,0,0,0x10000,SHIFT_ASR,2,NA,0,0,1,LT}, + {0xA049,INSTR_CMP,AL,AL,1,MAX_32BIT ,NA,0,0,MAX_32BIT ,SHIFT_ASR,1,NA,0,0,1,EQ}, + {0xA050,INSTR_CMP,AL,AL,1,MAX_32BIT ,NA,0,0,MAX_32BIT ,SHIFT_ASR,31,NA,0,0,1,LS}, + {0xA051,INSTR_CMP,AL,AL,1,0,NA,0,0,0x10000,SHIFT_ASR,1,NA,0,0,1,LS}, + {0xA052,INSTR_CMP,AL,AL,1,0x10000,NA,0,0,0x10000,SHIFT_ASR,1,NA,0,0,1,HI}, + {0xA053,INSTR_CMP,AL,AL,1,1,NA,0,0,0x10000,SHIFT_ASR,31,NA,0,0,1,HS}, + {0xA054,INSTR_CMP,AL,AL,1,1,NA,0,0,0x10000,SHIFT_ASR,16,NA,0,0,1,HS}, + {0xA055,INSTR_CMP,AL,AL,1,1,NA,0,0,MAX_32BIT ,SHIFT_ASR,1,NA,0,0,1,NE}, + {0xA056,INSTR_MUL,AL,AL,0,0,0x10000,0,0,0x10000,0,0,NA,1,0,0,0}, + {0xA057,INSTR_MUL,AL,AL,0,0,0x1000,0,0,0x10000,0,0,NA,1,0x10000000,0,0}, + {0xA058,INSTR_MUL,AL,AL,0,0,MAX_32BIT ,0,0,1,0,0,NA,1,MAX_32BIT ,0,0}, + {0xA059,INSTR_MLA,AL,AL,0,0x10000,0x10000,0,0,0x10000,0,0,NA,1,0x10000,0,0}, + {0xA060,INSTR_MLA,AL,AL,0,0x10000,0x1000,0,0,0x10000,0,0,NA,1,0x10010000,0,0}, + {0xA061,INSTR_MLA,AL,AL,1,1,MAX_32BIT ,0,0,1,0,0,NA,1,0,1,PL}, + {0xA062,INSTR_MLA,AL,AL,1,0,MAX_32BIT ,0,0,1,0,0,NA,1,MAX_32BIT ,1,MI}, + {0xA063,INSTR_SUB,AL,AL,1,1 << 16,NA,1,1 << 16,NA,NA,NA,NA,1,0,1,PL}, + {0xA064,INSTR_SUB,AL,AL,1,(1 << 16) + 1,NA,1,1 << 16,NA,NA,NA,NA,1,1,1,PL}, + {0xA065,INSTR_SUB,AL,AL,1,0,NA,1,1 << 16,NA,NA,NA,NA,1,(uint32_t)(0 - (1<<16)),1,MI}, + {0xA066,INSTR_SUB,MI,MI,0,2,NA,0,NA,1,NA,NA,2,1,1,0,NA}, + {0xA067,INSTR_SUB,EQ,MI,0,2,NA,0,NA,1,NA,NA,2,1,2,0,NA}, + {0xA068,INSTR_SUB,GT,GE,0,2,NA,1,1,NA,NA,NA,2,1,1,0,NA}, + {0xA069,INSTR_SUB,LT,GE,0,2,NA,1,1,NA,NA,NA,2,1,2,0,NA}, + {0xA070,INSTR_SUB,CS,HS,0,2,NA,1,1,NA,NA,NA,2,1,1,0,NA}, + {0xA071,INSTR_SUB,CC,HS,0,2,NA,1,1,NA,NA,NA,2,1,2,0,NA}, + {0xA072,INSTR_SUB,AL,AL,0,1,NA,1,1 << 16,0,0,0,NA,1,(uint32_t)(1 - (1 << 16)),0,NA}, + {0xA073,INSTR_SUB,AL,AL,0,MAX_32BIT,NA,1,1,0,0,0,NA,1,MAX_32BIT - 1,0,NA}, + {0xA074,INSTR_SUB,AL,AL,0,1,NA,1,1,0,0,0,NA,1,0,0,NA}, + {0xA075,INSTR_SUB,AL,AL,0,1,NA,0,NA,1 << 16,0,0,NA,1,(uint32_t)(1 - (1 << 16)),0,NA}, + {0xA076,INSTR_SUB,AL,AL,0,MAX_32BIT,NA,0,NA,1,0,0,NA,1,MAX_32BIT - 1,0,NA}, + {0xA077,INSTR_SUB,AL,AL,0,1,NA,0,NA,1,0,0,NA,1,0,0,NA}, + {0xA078,INSTR_SUB,AL,AL,0,1,NA,0,NA,1,SHIFT_LSL,16,NA,1,(uint32_t)(1 - (1 << 16)),0,NA}, + {0xA079,INSTR_SUB,AL,AL,0,0x80000001,NA,0,NA,MAX_32BIT ,SHIFT_LSL,31,NA,1,1,0,NA}, + {0xA080,INSTR_SUB,AL,AL,0,1,NA,0,NA,3,SHIFT_LSR,1,NA,1,0,0,NA}, + {0xA081,INSTR_SUB,AL,AL,0,1,NA,0,NA,MAX_32BIT ,SHIFT_LSR,31,NA,1,0,0,NA}, + {0xA082,INSTR_RSB,GT,GE,0,2,NA,1,0,NA,NA,NA,2,1,(uint32_t)-2,0,NA}, + {0xA083,INSTR_RSB,LT,GE,0,2,NA,1,0,NA,NA,NA,2,1,2,0,NA}, + {0xA084,INSTR_RSB,AL,AL,0,1,NA,1,1 << 16,NA,NA,NA,NA,1,(1 << 16) - 1,0,NA}, + {0xA085,INSTR_RSB,AL,AL,0,MAX_32BIT,NA,1,1,NA,NA,NA,NA,1,(uint32_t) (1 - MAX_32BIT),0,NA}, + {0xA086,INSTR_RSB,AL,AL,0,1,NA,1,1,NA,NA,NA,NA,1,0,0,NA}, + {0xA087,INSTR_RSB,AL,AL,0,1,NA,0,NA,1 << 16,0,0,NA,1,(1 << 16) - 1,0,NA}, + {0xA088,INSTR_RSB,AL,AL,0,MAX_32BIT,NA,0,NA,1,0,0,NA,1,(uint32_t) (1 - MAX_32BIT),0,NA}, + {0xA089,INSTR_RSB,AL,AL,0,1,NA,0,NA,1,0,0,NA,1,0,0,NA}, + {0xA090,INSTR_RSB,AL,AL,0,1,NA,0,NA,1,SHIFT_LSL,16,NA,1,(1 << 16) - 1,0,NA}, + {0xA091,INSTR_RSB,AL,AL,0,0x80000001,NA,0,NA,MAX_32BIT ,SHIFT_LSL,31,NA,1,(uint32_t)-1,0,NA}, + {0xA092,INSTR_RSB,AL,AL,0,1,NA,0,NA,3,SHIFT_LSR,1,NA,1,0,0,NA}, + {0xA093,INSTR_RSB,AL,AL,0,1,NA,0,NA,MAX_32BIT ,SHIFT_LSR,31,NA,1,0,0,NA}, + {0xA094,INSTR_MOV,AL,AL,0,NA,NA,1,0x80000001,NA,NA,NA,NA,1,0x80000001,0,0}, + {0xA095,INSTR_MOV,AL,AL,0,NA,NA,0,0,0x80000001,0,0,NA,1,0x80000001,0,0}, + {0xA096,INSTR_MOV,AL,AL,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,1,NA,1,MAX_32BIT -1,0,0}, + {0xA097,INSTR_MOV,AL,AL,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,31,NA,1,0x80000000,0,0}, + {0xA098,INSTR_MOV,AL,AL,0,NA,NA,0,0,3,SHIFT_LSR,1,NA,1,1,0,0}, + {0xA099,INSTR_MOV,AL,AL,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSR,31,NA,1,1,0,0}, + {0xA100,INSTR_MOV,AL,AL,0,NA,NA,0,0,3,SHIFT_ASR,1,NA,1,1,0,0}, + {0xA101,INSTR_MOV,AL,AL,0,NA,NA,0,0,MAX_32BIT ,SHIFT_ASR,31,NA,1,MAX_32BIT ,0,0}, + {0xA102,INSTR_MOV,AL,AL,0,NA,NA,0,0,3,SHIFT_ROR,1,NA,1,0x80000001,0,0}, + {0xA103,INSTR_MOV,AL,AL,0,NA,NA,0,0,0x80000001,SHIFT_ROR,31,NA,1,3,0,0}, + {0xA104,INSTR_MOV,AL,AL,1,NA,NA,0,0,MAX_32BIT -1,SHIFT_ASR,1,NA,1,MAX_32BIT,1,MI}, + {0xA105,INSTR_MOV,AL,AL,1,NA,NA,0,0,3,SHIFT_ASR,1,NA,1,1,1,PL}, + {0xA106,INSTR_MOV,PL,MI,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2,0,0}, + {0xA107,INSTR_MOV,MI,MI,0,NA,NA,0,0,0x80000001,0,0,2,1,0x80000001,0,0}, + {0xA108,INSTR_MOV,EQ,LT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2,0,0}, + {0xA109,INSTR_MOV,LT,LT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0x80000001,0,0}, + {0xA110,INSTR_MOV,GT,GE,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,1,2,1,MAX_32BIT -1,0,0}, + {0xA111,INSTR_MOV,EQ,GE,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,31,2,1,0x80000000,0,0}, + {0xA112,INSTR_MOV,LT,GE,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,31,2,1,2,0,0}, + {0xA113,INSTR_MOV,GT,LE,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,1,2,1,2,0,0}, + {0xA114,INSTR_MOV,EQ,LE,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0x80000001,0,0}, + {0xA115,INSTR_MOV,LT,LE,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,31,2,1,0x80000000,0,0}, + {0xA116,INSTR_MOV,EQ,GT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2,0,0}, + {0xA117,INSTR_MOV,GT,GT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0x80000001,0,0}, + {0xA118,INSTR_MOV,LE,GT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2,0,0}, + {0xA119,INSTR_MOV,EQ,GT,0,NA,NA,0,0,0x80000001,0,0,2,1,2,0,0}, + {0xA120,INSTR_MOV,GT,GT,0,NA,NA,0,0,0x80000001,0,0,2,1,0x80000001,0,0}, + {0xA121,INSTR_MOV,LE,GT,0,NA,NA,0,0,0x80000001,0,0,2,1,2,0,0}, + {0xA122,INSTR_MOV,EQ,GT,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,1,2,1,2,0,0}, + {0xA123,INSTR_MOV,GT,GT,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,1,2,1,MAX_32BIT -1,0,0}, + {0xA124,INSTR_MOV,LE,GT,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,1,2,1,2,0,0}, + {0xA125,INSTR_MOV,LO,HS,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2,0,0}, + {0xA126,INSTR_MOV,HS,HS,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0x80000001,0,0}, + {0xA127,INSTR_MVN,LO,HS,0,NA,NA,1,MAX_32BIT -1,NA,NA,NA,2,1,2,0,0}, + {0xA128,INSTR_MVN,HS,HS,0,NA,NA,1,MAX_32BIT -1,NA,NA,NA,2,1,1,0,0}, + {0xA129,INSTR_MVN,AL,AL,0,NA,NA,1,0,NA,NA,NA,2,1,MAX_32BIT,0,NA}, + {0xA130,INSTR_MVN,AL,AL,0,NA,NA,0,NA,MAX_32BIT -1,NA,0,2,1,1,0,NA}, + {0xA131,INSTR_MVN,AL,AL,0,NA,NA,0,NA,0x80000001,NA,0,2,1,0x7FFFFFFE,0,NA}, + {0xA132,INSTR_BIC,AL,AL,0,1,NA,1,MAX_32BIT ,NA,NA,NA,NA,1,0,0,0}, + {0xA133,INSTR_BIC,AL,AL,0,1,NA,1,MAX_32BIT -1,NA,NA,NA,NA,1,1,0,0}, + {0xA134,INSTR_BIC,AL,AL,0,1,NA,0,0,MAX_32BIT ,0,0,NA,1,0,0,0}, + {0xA135,INSTR_BIC,AL,AL,0,1,NA,0,0,MAX_32BIT -1,0,0,NA,1,1,0,0}, + {0xA136,INSTR_BIC,AL,AL,0,0xF0,NA,0,0,3,SHIFT_ASR,1,NA,1,0xF0,0,0}, + {0xA137,INSTR_BIC,AL,AL,0,0xF0,NA,0,0,MAX_32BIT ,SHIFT_ASR,31,NA,1,0,0,0}, + {0xA138,INSTR_SMULBB,AL,AL,0,NA,0xABCDFFFF,0,NA,0xABCD0001,NA,NA,NA,1,0xFFFFFFFF,0,0}, + {0xA139,INSTR_SMULBB,AL,AL,0,NA,0xABCD0001,0,NA,0xABCD0FFF,NA,NA,NA,1,0x00000FFF,0,0}, + {0xA140,INSTR_SMULBB,AL,AL,0,NA,0xABCD0001,0,NA,0xABCDFFFF,NA,NA,NA,1,0xFFFFFFFF,0,0}, + {0xA141,INSTR_SMULBB,AL,AL,0,NA,0xABCDFFFF,0,NA,0xABCDFFFF,NA,NA,NA,1,1,0,0}, + {0xA142,INSTR_SMULBT,AL,AL,0,NA,0xFFFFABCD,0,NA,0xABCD0001,NA,NA,NA,1,0xFFFFFFFF,0,0}, + {0xA143,INSTR_SMULBT,AL,AL,0,NA,0x0001ABCD,0,NA,0xABCD0FFF,NA,NA,NA,1,0x00000FFF,0,0}, + {0xA144,INSTR_SMULBT,AL,AL,0,NA,0x0001ABCD,0,NA,0xABCDFFFF,NA,NA,NA,1,0xFFFFFFFF,0,0}, + {0xA145,INSTR_SMULBT,AL,AL,0,NA,0xFFFFABCD,0,NA,0xABCDFFFF,NA,NA,NA,1,1,0,0}, + {0xA146,INSTR_SMULTB,AL,AL,0,NA,0xABCDFFFF,0,NA,0x0001ABCD,NA,NA,NA,1,0xFFFFFFFF,0,0}, + {0xA147,INSTR_SMULTB,AL,AL,0,NA,0xABCD0001,0,NA,0x0FFFABCD,NA,NA,NA,1,0x00000FFF,0,0}, + {0xA148,INSTR_SMULTB,AL,AL,0,NA,0xABCD0001,0,NA,0xFFFFABCD,NA,NA,NA,1,0xFFFFFFFF,0,0}, + {0xA149,INSTR_SMULTB,AL,AL,0,NA,0xABCDFFFF,0,NA,0xFFFFABCD,NA,NA,NA,1,1,0,0}, + {0xA150,INSTR_SMULTT,AL,AL,0,NA,0xFFFFABCD,0,NA,0x0001ABCD,NA,NA,NA,1,0xFFFFFFFF,0,0}, + {0xA151,INSTR_SMULTT,AL,AL,0,NA,0x0001ABCD,0,NA,0x0FFFABCD,NA,NA,NA,1,0x00000FFF,0,0}, + {0xA152,INSTR_SMULTT,AL,AL,0,NA,0x0001ABCD,0,NA,0xFFFFABCD,NA,NA,NA,1,0xFFFFFFFF,0,0}, + {0xA153,INSTR_SMULTT,AL,AL,0,NA,0xFFFFABCD,0,NA,0xFFFFABCD,NA,NA,NA,1,1,0,0}, + {0xA154,INSTR_SMULWB,AL,AL,0,NA,0xABCDFFFF,0,NA,0x0001ABCD,NA,NA,NA,1,0xFFFFFFFE,0,0}, + {0xA155,INSTR_SMULWB,AL,AL,0,NA,0xABCD0001,0,NA,0x0FFFABCD,NA,NA,NA,1,0x00000FFF,0,0}, + {0xA156,INSTR_SMULWB,AL,AL,0,NA,0xABCD0001,0,NA,0xFFFFABCD,NA,NA,NA,1,0xFFFFFFFF,0,0}, + {0xA157,INSTR_SMULWB,AL,AL,0,NA,0xABCDFFFF,0,NA,0xFFFFABCD,NA,NA,NA,1,0,0,0}, + {0xA158,INSTR_SMULWT,AL,AL,0,NA,0xFFFFABCD,0,NA,0x0001ABCD,NA,NA,NA,1,0xFFFFFFFE,0,0}, + {0xA159,INSTR_SMULWT,AL,AL,0,NA,0x0001ABCD,0,NA,0x0FFFABCD,NA,NA,NA,1,0x00000FFF,0,0}, + {0xA160,INSTR_SMULWT,AL,AL,0,NA,0x0001ABCD,0,NA,0xFFFFABCD,NA,NA,NA,1,0xFFFFFFFF,0,0}, + {0xA161,INSTR_SMULWT,AL,AL,0,NA,0xFFFFABCD,0,NA,0xFFFFABCD,NA,NA,NA,1,0,0,0}, + {0xA162,INSTR_SMLABB,AL,AL,0,1,0xABCDFFFF,0,NA,0xABCD0001,NA,NA,NA,1,0,0,0}, + {0xA163,INSTR_SMLABB,AL,AL,0,1,0xABCD0001,0,NA,0xABCD0FFF,NA,NA,NA,1,0x00001000,0,0}, + {0xA164,INSTR_SMLABB,AL,AL,0,0xFFFFFFFF,0xABCD0001,0,NA,0xABCDFFFF,NA,NA,NA,1,0xFFFFFFFE,0,0}, + {0xA165,INSTR_SMLABB,AL,AL,0,0xFFFFFFFF,0xABCDFFFF,0,NA,0xABCDFFFF,NA,NA,NA,1,0,0,0}, + {0xA166,INSTR_UXTB16,AL,AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,0,NA,1,0x00CD0001,0,0}, + {0xA167,INSTR_UXTB16,AL,AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,1,NA,1,0x00AB00EF,0,0}, + {0xA168,INSTR_UXTB16,AL,AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,2,NA,1,0x000100CD,0,0}, + {0xA169,INSTR_UXTB16,AL,AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,3,NA,1,0x00EF00AB,0,0}, + {0xA170,INSTR_UBFX,AL,AL,0,0xABCDEF01,4,0,NA,24,NA,NA,NA,1,0x00BCDEF0,0,0}, + {0xA171,INSTR_UBFX,AL,AL,0,0xABCDEF01,1,0,NA,2,NA,NA,NA,1,0,0,0}, + {0xA172,INSTR_UBFX,AL,AL,0,0xABCDEF01,16,0,NA,8,NA,NA,NA,1,0xCD,0,0}, + {0xA173,INSTR_UBFX,AL,AL,0,0xABCDEF01,31,0,NA,1,NA,NA,NA,1,1,0,0}, + {0xA174,INSTR_ADDR_ADD,AL,AL,0,0xCFFFFFFFF,NA,0,NA,0x1,SHIFT_LSL,1,NA,1,0xD00000001,0,0}, + {0xA175,INSTR_ADDR_ADD,AL,AL,0,0x01,NA,0,NA,0x1,SHIFT_LSL,2,NA,1,0x5,0,0}, + {0xA176,INSTR_ADDR_ADD,AL,AL,0,0xCFFFFFFFF,NA,0,NA,0x1,NA,0,NA,1,0xD00000000,0,0}, + {0xA177,INSTR_ADDR_SUB,AL,AL,0,0xD00000001,NA,0,NA,0x010000,SHIFT_LSR,15,NA,1,0xCFFFFFFFF,0,0}, + {0xA178,INSTR_ADDR_SUB,AL,AL,0,0xCFFFFFFFF,NA,0,NA,0x020000,SHIFT_LSR,15,NA,1,0xCFFFFFFFB,0,0}, + {0xA179,INSTR_ADDR_SUB,AL,AL,0,3,NA,0,NA,0x010000,SHIFT_LSR,15,NA,1,1,0,0}, +}; + +dataTransferTest_t dataTransferTests [] = +{ + {0xB000,INSTR_LDR,AL,AL,1,24,0xABCDEF0123456789,0,REG_SCALE_OFFSET,24,NA,NA,NA,NA,NA,0x23456789,0,0,NA,NA,NA}, + {0xB001,INSTR_LDR,AL,AL,1,4064,0xABCDEF0123456789,0,IMM12_OFFSET,NA,4068,0,1,0,NA,0xABCDEF01,0,0,NA,NA,NA}, + {0xB002,INSTR_LDR,AL,AL,1,0,0xABCDEF0123456789,0,IMM12_OFFSET,NA,4,1,0,1,NA,0x23456789,4,0,NA,NA,NA}, + {0xB003,INSTR_LDR,AL,AL,1,0,0xABCDEF0123456789,0,NO_OFFSET,NA,NA,0,0,0,NA,0x23456789,0,0,NA,NA,NA}, + {0xB004,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,0,REG_SCALE_OFFSET,4064,NA,NA,NA,NA,NA,0x89,0,0,NA,NA,NA}, + {0xB005,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,0,IMM12_OFFSET,NA,4065,0,1,0,NA,0x67,0,0,NA,NA,NA}, + {0xB006,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,0,0,1,0,NA,0x67,4065,0,NA,NA,NA}, + {0xB007,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,1,0,1,0,NA,0x45,4065,0,NA,NA,NA}, + {0xB008,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,2,0,1,0,NA,0x23,4065,0,NA,NA,NA}, + {0xB009,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,1,1,0,1,NA,0x67,4066,0,NA,NA,NA}, + {0xB010,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,0,NO_OFFSET,NA,NA,0,0,0,NA,0x89,0,0,NA,NA,NA}, + {0xB011,INSTR_LDRH,AL,AL,1,0,0xABCDEF0123456789,0,IMM8_OFFSET,NA,2,1,0,1,NA,0x6789,2,0,NA,NA,NA}, + {0xB012,INSTR_LDRH,AL,AL,1,4064,0xABCDEF0123456789,0,REG_OFFSET,4064,0,0,1,0,NA,0x6789,0,0,NA,NA,NA}, + {0xB013,INSTR_LDRH,AL,AL,1,4064,0xABCDEF0123456789,0,REG_OFFSET,4066,0,0,1,0,NA,0x2345,0,0,NA,NA,NA}, + {0xB014,INSTR_LDRH,AL,AL,1,0,0xABCDEF0123456789,0,NO_OFFSET,NA,0,0,0,0,NA,0x6789,0,0,NA,NA,NA}, + {0xB015,INSTR_LDRH,AL,AL,1,0,0xABCDEF0123456789,2,NO_OFFSET,NA,0,0,0,0,NA,0x2345,2,0,NA,NA,NA}, + {0xB016,INSTR_ADDR_LDR,AL,AL,1,4064,0xABCDEF0123456789,0,IMM12_OFFSET,NA,4064,0,1,0,NA,0xABCDEF0123456789,0,0,NA,NA,NA}, + {0xB017,INSTR_STR,AL,AL,1,2,0xDEADBEEFDEADBEEF,4,IMM12_OFFSET,NA,4,1,0,1,0xABCDEF0123456789,0xABCDEF0123456789,8,1,2,8,0xDEAD23456789BEEF}, + {0xB018,INSTR_STR,AL,AL,1,2,0xDEADBEEFDEADBEEF,4,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4,1,2,8,0xDEAD23456789BEEF}, + {0xB019,INSTR_STR,AL,AL,1,4066,0xDEADBEEFDEADBEEF,4,IMM12_OFFSET,NA,4064,0,1,0,0xABCDEF0123456789,0xABCDEF0123456789,4,1,4066,8,0xDEAD23456789BEEF}, + {0xB020,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,0,0,1,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEFDEAD89EF}, + {0xB021,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,1,0,1,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEFDE89BEEF}, + {0xB022,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,2,0,1,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEF89ADBEEF}, + {0xB023,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,4,1,0,1,0xABCDEF0123456789,0xABCDEF0123456789,5,1,0,8,0xDEADBEEFDEAD89EF}, + {0xB024,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEFDEAD89EF}, + {0xB025,INSTR_STRH,AL,AL,1,4066,0xDEADBEEFDEADBEEF,4070,IMM12_OFFSET,NA,2,1,0,1,0xABCDEF0123456789,0xABCDEF0123456789,4072,1,4066,8,0xDEAD6789DEADBEEF}, + {0xB026,INSTR_STRH,AL,AL,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEAD6789DEADBEEF}, + {0xB027,INSTR_STRH,EQ,NE,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEADBEEFDEADBEEF}, + {0xB028,INSTR_STRH,NE,NE,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEAD6789DEADBEEF}, + {0xB029,INSTR_STRH,NE,EQ,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEADBEEFDEADBEEF}, + {0xB030,INSTR_STRH,EQ,EQ,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEAD6789DEADBEEF}, + {0xB031,INSTR_STRH,HI,LS,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEADBEEFDEADBEEF}, + {0xB032,INSTR_STRH,LS,LS,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEAD6789DEADBEEF}, + {0xB033,INSTR_STRH,LS,HI,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEADBEEFDEADBEEF}, + {0xB034,INSTR_STRH,HI,HI,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEAD6789DEADBEEF}, + {0xB035,INSTR_STRH,CC,HS,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEADBEEFDEADBEEF}, + {0xB036,INSTR_STRH,CS,HS,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEAD6789DEADBEEF}, + {0xB037,INSTR_STRH,GE,LT,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEADBEEFDEADBEEF}, + {0xB038,INSTR_STRH,LT,LT,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEAD6789DEADBEEF}, + {0xB039,INSTR_ADDR_STR,AL,AL,1,4064,0xDEADBEEFDEADBEEF,4,IMM12_OFFSET,NA,4060,0,1,0,0xABCDEF0123456789,0xABCDEF0123456789,4,1,4064,8,0xABCDEF0123456789}, +}; + + +int flushcache() +{ + const long base = long(instrMem); + const long curr = base + long(instrMemSize); + return cacheflush(base, curr, 0); +} +void dataOpTest(dataOpTest_t test, ARMAssemblerInterface *a64asm, uint32_t Rd = 0, + uint32_t Rn = 1, uint32_t Rm = 2, uint32_t Rs = 3) +{ + int64_t regs[NUM_REGS] = {0}; + int32_t flags[NUM_FLAGS] = {0}; + int64_t savedRegs[NUM_REGS] = {0}; + uint32_t i; + uint32_t op2; + + for(i = 0; i < NUM_REGS; ++i) + { + regs[i] = i; + } + + regs[Rd] = test.RdValue; + regs[Rn] = test.RnValue; + regs[Rs] = test.RsValue; + flags[test.preFlag] = 1; + a64asm->reset(); + a64asm->prolog(); + if(test.immediate == true) + { + op2 = a64asm->imm(test.immValue); + } + else if(test.immediate == false && test.shiftAmount == 0) + { + op2 = Rm; + regs[Rm] = test.RmValue; + } + else + { + op2 = a64asm->reg_imm(Rm, test.shiftMode, test.shiftAmount); + regs[Rm] = test.RmValue; + } + switch(test.op) + { + case INSTR_ADD: a64asm->ADD(test.cond, test.setFlags, Rd,Rn,op2); break; + case INSTR_SUB: a64asm->SUB(test.cond, test.setFlags, Rd,Rn,op2); break; + case INSTR_RSB: a64asm->RSB(test.cond, test.setFlags, Rd,Rn,op2); break; + case INSTR_AND: a64asm->AND(test.cond, test.setFlags, Rd,Rn,op2); break; + case INSTR_ORR: a64asm->ORR(test.cond, test.setFlags, Rd,Rn,op2); break; + case INSTR_BIC: a64asm->BIC(test.cond, test.setFlags, Rd,Rn,op2); break; + case INSTR_MUL: a64asm->MUL(test.cond, test.setFlags, Rd,Rm,Rs); break; + case INSTR_MLA: a64asm->MLA(test.cond, test.setFlags, Rd,Rm,Rs,Rn); break; + case INSTR_CMP: a64asm->CMP(test.cond, Rn,op2); break; + case INSTR_MOV: a64asm->MOV(test.cond, test.setFlags,Rd,op2); break; + case INSTR_MVN: a64asm->MVN(test.cond, test.setFlags,Rd,op2); break; + case INSTR_SMULBB:a64asm->SMULBB(test.cond, Rd,Rm,Rs); break; + case INSTR_SMULBT:a64asm->SMULBT(test.cond, Rd,Rm,Rs); break; + case INSTR_SMULTB:a64asm->SMULTB(test.cond, Rd,Rm,Rs); break; + case INSTR_SMULTT:a64asm->SMULTT(test.cond, Rd,Rm,Rs); break; + case INSTR_SMULWB:a64asm->SMULWB(test.cond, Rd,Rm,Rs); break; + case INSTR_SMULWT:a64asm->SMULWT(test.cond, Rd,Rm,Rs); break; + case INSTR_SMLABB:a64asm->SMLABB(test.cond, Rd,Rm,Rs,Rn); break; + case INSTR_UXTB16:a64asm->UXTB16(test.cond, Rd,Rm,test.shiftAmount); break; + case INSTR_UBFX: + { + int32_t lsb = test.RsValue; + int32_t width = test.RmValue; + a64asm->UBFX(test.cond, Rd,Rn,lsb, width); + break; + } + case INSTR_ADDR_ADD: a64asm->ADDR_ADD(test.cond, test.setFlags, Rd,Rn,op2); break; + case INSTR_ADDR_SUB: a64asm->ADDR_SUB(test.cond, test.setFlags, Rd,Rn,op2); break; + default: printf("Error"); return; + } + a64asm->epilog(0); + flushcache(); + + asm_function_t asm_function = (asm_function_t)(instrMem); + + for(i = 0; i < NUM_REGS; ++i) + savedRegs[i] = regs[i]; + + asm_test_jacket(asm_function, regs, flags); + + /* Check if all regs except Rd is same */ + for(i = 0; i < NUM_REGS; ++i) + { + if(i == Rd) continue; + if(regs[i] != savedRegs[i]) + { + printf("Test %x failed Reg(%d) tampered Expected(0x%"PRIx64")," + "Actual(0x%"PRIx64") t\n", test.id, i, savedRegs[i], regs[i]); + return; + } + } + + if(test.checkRd == 1 && (uint64_t)regs[Rd] != test.postRdValue) + { + printf("Test %x failed, Expected(%"PRIx64"), Actual(%"PRIx64")\n", + test.id, test.postRdValue, regs[Rd]); + } + else if(test.checkFlag == 1 && flags[test.postFlag] == 0) + { + printf("Test %x failed Flag(%s) NOT set\n", + test.id,cc_code[test.postFlag]); + } + else + { + printf("Test %x passed\n", test.id); + } +} + + +void dataTransferTest(dataTransferTest_t test, ARMAssemblerInterface *a64asm, + uint32_t Rd = 0, uint32_t Rn = 1,uint32_t Rm = 2) +{ + int64_t regs[NUM_REGS] = {0}; + int64_t savedRegs[NUM_REGS] = {0}; + int32_t flags[NUM_FLAGS] = {0}; + uint32_t i; + for(i = 0; i < NUM_REGS; ++i) + { + regs[i] = i; + } + + uint32_t op2; + + regs[Rd] = test.RdValue; + regs[Rn] = (uint64_t)(&dataMem[test.RnValue]); + regs[Rm] = test.RmValue; + flags[test.preFlag] = 1; + + if(test.setMem == true) + { + unsigned char *mem = (unsigned char *)&dataMem[test.memOffset]; + uint64_t value = test.memValue; + for(int j = 0; j < 8; ++j) + { + mem[j] = value & 0x00FF; + value >>= 8; + } + } + a64asm->reset(); + a64asm->prolog(); + if(test.offsetType == REG_SCALE_OFFSET) + { + op2 = a64asm->reg_scale_pre(Rm); + } + else if(test.offsetType == REG_OFFSET) + { + op2 = a64asm->reg_pre(Rm); + } + else if(test.offsetType == IMM12_OFFSET && test.preIndex == true) + { + op2 = a64asm->immed12_pre(test.immValue, test.writeBack); + } + else if(test.offsetType == IMM12_OFFSET && test.postIndex == true) + { + op2 = a64asm->immed12_post(test.immValue); + } + else if(test.offsetType == IMM8_OFFSET && test.preIndex == true) + { + op2 = a64asm->immed8_pre(test.immValue, test.writeBack); + } + else if(test.offsetType == IMM8_OFFSET && test.postIndex == true) + { + op2 = a64asm->immed8_post(test.immValue); + } + else if(test.offsetType == NO_OFFSET) + { + op2 = a64asm->__immed12_pre(0); + } + else + { + printf("Error - Unknown offset\n"); return; + } + + switch(test.op) + { + case INSTR_LDR: a64asm->LDR(test.cond, Rd,Rn,op2); break; + case INSTR_LDRB: a64asm->LDRB(test.cond, Rd,Rn,op2); break; + case INSTR_LDRH: a64asm->LDRH(test.cond, Rd,Rn,op2); break; + case INSTR_ADDR_LDR: a64asm->ADDR_LDR(test.cond, Rd,Rn,op2); break; + case INSTR_STR: a64asm->STR(test.cond, Rd,Rn,op2); break; + case INSTR_STRB: a64asm->STRB(test.cond, Rd,Rn,op2); break; + case INSTR_STRH: a64asm->STRH(test.cond, Rd,Rn,op2); break; + case INSTR_ADDR_STR: a64asm->ADDR_STR(test.cond, Rd,Rn,op2); break; + default: printf("Error"); return; + } + a64asm->epilog(0); + flushcache(); + + asm_function_t asm_function = (asm_function_t)(instrMem); + + for(i = 0; i < NUM_REGS; ++i) + savedRegs[i] = regs[i]; + + + asm_test_jacket(asm_function, regs, flags); + + /* Check if all regs except Rd/Rn are same */ + for(i = 0; i < NUM_REGS; ++i) + { + if(i == Rd || i == Rn) continue; + if(regs[i] != savedRegs[i]) + { + printf("Test %x failed Reg(%d) tampered" + " Expected(0x%"PRIx64"), Actual(0x%"PRIx64") t\n", + test.id, i, savedRegs[i], regs[i]); + return; + } + } + + if((uint64_t)regs[Rd] != test.postRdValue) + { + printf("Test %x failed, " + "Expected in Rd(0x%"PRIx64"), Actual(0x%"PRIx64")\n", + test.id, test.postRdValue, regs[Rd]); + } + else if((uint64_t)regs[Rn] != (uint64_t)(&dataMem[test.postRnValue])) + { + printf("Test %x failed, " + "Expected in Rn(0x%"PRIx64"), Actual(0x%"PRIx64")\n", + test.id, test.postRnValue, regs[Rn] - (uint64_t)dataMem); + } + else if(test.checkMem == true) + { + unsigned char *addr = (unsigned char *)&dataMem[test.postMemOffset]; + uint64_t value; + value = 0; + for(uint32_t j = 0; j < test.postMemLength; ++j) + value = (value << 8) | addr[test.postMemLength-j-1]; + if(value != test.postMemValue) + { + printf("Test %x failed, " + "Expected in Mem(0x%"PRIx64"), Actual(0x%"PRIx64")\n", + test.id, test.postMemValue, value); + } + else + { + printf("Test %x passed\n", test.id); + } + } + else + { + printf("Test %x passed\n", test.id); + } +} + +void dataTransferLDMSTM(ARMAssemblerInterface *a64asm) +{ + int64_t regs[NUM_REGS] = {0}; + int32_t flags[NUM_FLAGS] = {0}; + const uint32_t numArmv7Regs = 16; + + uint32_t Rn = ARMAssemblerInterface::SP; + + uint32_t patterns[] = + { + 0x5A03, + 0x4CF0, + 0x1EA6, + 0x0DBF, + }; + + uint32_t i, j; + for(i = 0; i < sizeof(patterns)/sizeof(uint32_t); ++i) + { + for(j = 0; j < NUM_REGS; ++j) + { + regs[j] = j; + } + a64asm->reset(); + a64asm->prolog(); + a64asm->STM(AL,ARMAssemblerInterface::DB,Rn,1,patterns[i]); + for(j = 0; j < numArmv7Regs; ++j) + { + uint32_t op2 = a64asm->imm(0x31); + a64asm->MOV(AL, 0,j,op2); + } + a64asm->LDM(AL,ARMAssemblerInterface::IA,Rn,1,patterns[i]); + a64asm->epilog(0); + flushcache(); + + asm_function_t asm_function = (asm_function_t)(instrMem); + asm_test_jacket(asm_function, regs, flags); + + for(j = 0; j < numArmv7Regs; ++j) + { + if((1 << j) & patterns[i]) + { + if(regs[j] != j) + { + printf("LDM/STM Test %x failed " + "Reg%d expected(0x%x) Actual(0x%"PRIx64") \n", + patterns[i],j,j,regs[j]); + break; + } + } + } + if(j == numArmv7Regs) + printf("LDM/STM Test %x passed\n", patterns[i]); + } +} + +int main(void) +{ + uint32_t i; + + /* Allocate memory to store instructions generated by ArmToArm64Assembler */ + { + int fd = ashmem_create_region("code cache", instrMemSize); + if(fd < 0) + printf("Creating code cache, ashmem_create_region " + "failed with error '%s'", strerror(errno)); + instrMem = mmap(NULL, instrMemSize, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE, fd, 0); + } + + ArmToArm64Assembler a64asm(instrMem); + + if(TESTS_DATAOP_ENABLE) + { + printf("Running data processing tests\n"); + for(i = 0; i < sizeof(dataOpTests)/sizeof(dataOpTest_t); ++i) + dataOpTest(dataOpTests[i], &a64asm); + } + + if(TESTS_DATATRANSFER_ENABLE) + { + printf("Running data transfer tests\n"); + for(i = 0; i < sizeof(dataTransferTests)/sizeof(dataTransferTest_t); ++i) + dataTransferTest(dataTransferTests[i], &a64asm); + } + + if(TESTS_LDMSTM_ENABLE) + { + printf("Running LDM/STM tests\n"); + dataTransferLDMSTM(&a64asm); + } + + + if(TESTS_REG_CORRUPTION_ENABLE) + { + uint32_t reg_list[] = {0,1,12,14}; + uint32_t Rd, Rm, Rs, Rn; + uint32_t i; + uint32_t numRegs = sizeof(reg_list)/sizeof(uint32_t); + + printf("Running Register corruption tests\n"); + for(i = 0; i < sizeof(dataOpTests)/sizeof(dataOpTest_t); ++i) + { + for(Rd = 0; Rd < numRegs; ++Rd) + { + for(Rn = 0; Rn < numRegs; ++Rn) + { + for(Rm = 0; Rm < numRegs; ++Rm) + { + for(Rs = 0; Rs < numRegs;++Rs) + { + if(Rd == Rn || Rd == Rm || Rd == Rs) continue; + if(Rn == Rm || Rn == Rs) continue; + if(Rm == Rs) continue; + printf("Testing combination Rd(%d), Rn(%d)," + " Rm(%d), Rs(%d): ", + reg_list[Rd], reg_list[Rn], reg_list[Rm], reg_list[Rs]); + dataOpTest(dataOpTests[i], &a64asm, reg_list[Rd], + reg_list[Rn], reg_list[Rm], reg_list[Rs]); + } + } + } + } + } + } + return 0; +} diff --git a/libpixelflinger/tests/arch-arm64/assembler/asm_test_jacket.S b/libpixelflinger/tests/arch-arm64/assembler/asm_test_jacket.S new file mode 100644 index 0000000..a1392c2 --- /dev/null +++ b/libpixelflinger/tests/arch-arm64/assembler/asm_test_jacket.S @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + .text + .align + + .global asm_test_jacket + + // Set the register and flag values + // Calls the asm function + // Reads the register/flag values to output register + + // Parameters + // X0 - Function to jump + // X1 - register values array + // X2 - flag values array +asm_test_jacket: + // Save registers to stack + stp x29, x30, [sp,#-16]! + stp x27, x28, [sp,#-16]! + + mov x30, x0 + mov x28, x1 + mov x27, x2 + + //Set the flags based on flag array + //EQ + ldr w0, [x27,#0] + cmp w0, #1 + b.ne bt_aeq + cmp w0,#1 + b bt_end +bt_aeq: + + //NE + ldr w0, [x27,#4] + cmp w0, #1 + b.ne bt_ane + cmp w0,#2 + b bt_end +bt_ane: + + //CS + ldr w0, [x27,#8] + cmp w0, #1 + b.ne bt_acs + cmp w0,#0 + b bt_end +bt_acs: + + //CC + ldr w0, [x27,#12] + cmp w0, #1 + b.ne bt_acc + cmp w0,#2 + b bt_end +bt_acc: + + //MI + ldr w0, [x27,#16] + cmp w0, #1 + b.ne bt_ami + subs w0,w0,#2 + b bt_end +bt_ami: + + //PL + ldr w0, [x27,#20] + cmp w0, #1 + b.ne bt_apl + subs w0,w0,#0 + b bt_end +bt_apl: + //HI - (C==1) && (Z==0) + ldr w0, [x27,#32] + cmp w0, #1 + b.ne bt_ahi + cmp w0,#0 + b bt_end +bt_ahi: + + //LS - (C==0) || (Z==1) + ldr w0, [x27,#36] + cmp w0, #1 + b.ne bt_als + cmp w0,#1 + b bt_end +bt_als: + + //GE + ldr w0, [x27,#40] + cmp w0, #1 + b.ne bt_age + cmp w0,#0 + b bt_end +bt_age: + + //LT + ldr w0, [x27,#44] + cmp w0, #1 + b.ne bt_alt + cmp w0,#2 + b bt_end +bt_alt: + + //GT + ldr w0, [x27,#48] + cmp w0, #1 + b.ne bt_agt + cmp w0,#0 + b bt_end +bt_agt: + + //LE + ldr w0, [x27,#52] + cmp w0, #1 + b.ne bt_ale + cmp w0,#2 + b bt_end +bt_ale: + + +bt_end: + + // Load the registers from reg array + ldr x0, [x28,#0] + ldr x1, [x28,#8] + ldr x2, [x28,#16] + ldr x3, [x28,#24] + ldr x4, [x28,#32] + ldr x5, [x28,#40] + ldr x6, [x28,#48] + ldr x7, [x28,#56] + ldr x8, [x28,#64] + ldr x9, [x28,#72] + ldr x10, [x28,#80] + ldr x11, [x28,#88] + ldr x12, [x28,#96] + ldr x14, [x28,#112] + + // Call the function + blr X30 + + // Save the registers to reg array + str x0, [x28,#0] + str x1, [x28,#8] + str x2, [x28,#16] + str x3, [x28,#24] + str x4, [x28,#32] + str x5, [x28,#40] + str x6, [x28,#48] + str x7, [x28,#56] + str x8, [x28,#64] + str x9, [x28,#72] + str x10, [x28,#80] + str x11, [x28,#88] + str x12, [x28,#96] + str x14, [x28,#112] + + //Set the flags array based on result flags + movz w0, #0 + movz w1, #1 + csel w2, w1, w0, EQ + str w2, [x27,#0] + csel w2, w1, w0, NE + str w2, [x27,#4] + csel w2, w1, w0, CS + str w2, [x27,#8] + csel w2, w1, w0, CC + str w2, [x27,#12] + csel w2, w1, w0, MI + str w2, [x27,#16] + csel w2, w1, w0, PL + str w2, [x27,#20] + csel w2, w1, w0, VS + str w2, [x27,#24] + csel w2, w1, w0, VC + str w2, [x27,#28] + csel w2, w1, w0, HI + str w2, [x27,#32] + csel w2, w1, w0, LS + str w2, [x27,#36] + csel w2, w1, w0, GE + str w2, [x27,#40] + csel w2, w1, w0, LT + str w2, [x27,#44] + csel w2, w1, w0, GT + str w2, [x27,#48] + csel w2, w1, w0, LE + str w2, [x27,#52] + + // Restore registers from stack + ldp x27, x28, [sp],#16 + ldp x29, x30, [sp],#16 + ret + diff --git a/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk b/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk new file mode 100644 index 0000000..ac890c7 --- /dev/null +++ b/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk @@ -0,0 +1,16 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + col32cb16blend_test.c \ + ../../../arch-arm64/col32cb16blend.S + +LOCAL_SHARED_LIBRARIES := + +LOCAL_C_INCLUDES := + +LOCAL_MODULE:= test-pixelflinger-arm64-col32cb16blend + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) diff --git a/libpixelflinger/tests/arch-arm64/col32cb16blend/col32cb16blend_test.c b/libpixelflinger/tests/arch-arm64/col32cb16blend/col32cb16blend_test.c new file mode 100644 index 0000000..c6a3017 --- /dev/null +++ b/libpixelflinger/tests/arch-arm64/col32cb16blend/col32cb16blend_test.c @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> + + +#define ARGB_8888_MAX 0xFFFFFFFF +#define ARGB_8888_MIN 0x00000000 +#define RGB_565_MAX 0xFFFF +#define RGB_565_MIN 0x0000 + +struct test_t +{ + char name[256]; + uint32_t dst_color; + uint32_t src_color; + size_t count; +}; + +struct test_t tests[] = +{ + {"Count 1, Src=Max, Dst=Min", ARGB_8888_MAX, RGB_565_MIN, 1}, + {"Count 2, Src=Min, Dst=Max", ARGB_8888_MIN, RGB_565_MAX, 2}, + {"Count 3, Src=Max, Dst=Max", ARGB_8888_MAX, RGB_565_MAX, 3}, + {"Count 4, Src=Min, Dst=Min", ARGB_8888_MAX, RGB_565_MAX, 4}, + {"Count 1, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 1}, + {"Count 2, Src=Rand, Dst=Rand", 0xABCDEF12, 0x2345, 2}, + {"Count 3, Src=Rand, Dst=Rand", 0x11111111, 0xEDFE, 3}, + {"Count 4, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 4}, + {"Count 5, Src=Rand, Dst=Rand", 0xEFEFFEFE, 0xFACC, 5}, + {"Count 10, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 10} +}; + +void scanline_col32cb16blend_arm64(uint16_t *dst, int32_t src, size_t count); +void scanline_col32cb16blend_c(uint16_t * dst, int32_t src, size_t count) +{ + int srcAlpha = (src>>24); + int f = 0x100 - (srcAlpha + (srcAlpha>>7)); + while (count--) + { + uint16_t d = *dst; + int dstR = (d>>11)&0x1f; + int dstG = (d>>5)&0x3f; + int dstB = (d)&0x1f; + int srcR = (src >> ( 3))&0x1F; + int srcG = (src >> ( 8+2))&0x3F; + int srcB = (src >> (16+3))&0x1F; + srcR += (f*dstR)>>8; + srcG += (f*dstG)>>8; + srcB += (f*dstB)>>8; + *dst++ = (uint16_t)((srcR<<11)|(srcG<<5)|srcB); + } +} + +void scanline_col32cb16blend_test() +{ + uint16_t dst_c[16], dst_asm[16]; + uint32_t i, j; + + for(i = 0; i < sizeof(tests)/sizeof(struct test_t); ++i) + { + struct test_t test = tests[i]; + + printf("Testing - %s:",test.name); + + memset(dst_c, 0, sizeof(dst_c)); + memset(dst_asm, 0, sizeof(dst_asm)); + + for(j = 0; j < test.count; ++j) + { + dst_c[j] = test.dst_color; + dst_asm[j] = test.dst_color; + } + + + scanline_col32cb16blend_c(dst_c, test.src_color, test.count); + scanline_col32cb16blend_arm64(dst_asm, test.src_color, test.count); + + + if(memcmp(dst_c, dst_asm, sizeof(dst_c)) == 0) + printf("Passed\n"); + else + printf("Failed\n"); + + for(j = 0; j < test.count; ++j) + { + printf("dst_c[%d] = %x, dst_asm[%d] = %x \n", j, dst_c[j], j, dst_asm[j]); + } + } +} + +int main() +{ + scanline_col32cb16blend_test(); + return 0; +} diff --git a/libpixelflinger/tests/arch-arm64/disassembler/Android.mk b/libpixelflinger/tests/arch-arm64/disassembler/Android.mk new file mode 100644 index 0000000..baf4070 --- /dev/null +++ b/libpixelflinger/tests/arch-arm64/disassembler/Android.mk @@ -0,0 +1,17 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + arm64_diassembler_test.cpp \ + ../../../codeflinger/Arm64Disassembler.cpp + +LOCAL_SHARED_LIBRARIES := + +LOCAL_C_INCLUDES := \ + system/core/libpixelflinger/codeflinger + +LOCAL_MODULE:= test-pixelflinger-arm64-disassembler-test + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) diff --git a/libpixelflinger/tests/arch-arm64/disassembler/arm64_diassembler_test.cpp b/libpixelflinger/tests/arch-arm64/disassembler/arm64_diassembler_test.cpp new file mode 100644 index 0000000..af3183b --- /dev/null +++ b/libpixelflinger/tests/arch-arm64/disassembler/arm64_diassembler_test.cpp @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include <stdio.h> +#include <inttypes.h> +#include <string.h> + +int arm64_disassemble(uint32_t code, char* instr); + +struct test_table_entry_t +{ + uint32_t code; + const char *instr; +}; +static test_table_entry_t test_table [] = +{ + { 0x91000240, "add x0, x18, #0x0, lsl #0" }, + { 0x9140041f, "add sp, x0, #0x1, lsl #12" }, + { 0x917ffff2, "add x18, sp, #0xfff, lsl #12" }, + + { 0xd13ffe40, "sub x0, x18, #0xfff, lsl #0" }, + { 0xd140001f, "sub sp, x0, #0x0, lsl #12" }, + { 0xd14007f2, "sub x18, sp, #0x1, lsl #12" }, + + { 0x8b1e0200, "add x0, x16, x30, lsl #0" }, + { 0x8b507fdf, "add xzr, x30, x16, lsr #31" }, + { 0x8b8043f0, "add x16, xzr, x0, asr #16" }, + { 0x8b5f401e, "add x30, x0, xzr, lsr #16" }, + + + { 0x4b1e0200, "sub w0, w16, w30, lsl #0" }, + { 0x4b507fdf, "sub wzr, w30, w16, lsr #31" }, + { 0x4b8043f0, "sub w16, wzr, w0, asr #16" }, + { 0x4b5f401e, "sub w30, w0, wzr, lsr #16" }, + + { 0x6b1e0200, "subs w0, w16, w30, lsl #0" }, + { 0x6b507fdf, "subs wzr, w30, w16, lsr #31" }, + { 0x6b8043f0, "subs w16, wzr, w0, asr #16" }, + { 0x6b5f401e, "subs w30, w0, wzr, lsr #16" }, + + { 0x0a1e0200, "and w0, w16, w30, lsl #0" }, + { 0x0a507fdf, "and wzr, w30, w16, lsr #31" }, + { 0x0a8043f0, "and w16, wzr, w0, asr #16" }, + { 0x0adf401e, "and w30, w0, wzr, ror #16" }, + + { 0x2a1e0200, "orr w0, w16, w30, lsl #0" }, + { 0x2a507fdf, "orr wzr, w30, w16, lsr #31" }, + { 0x2a8043f0, "orr w16, wzr, w0, asr #16" }, + { 0x2adf401e, "orr w30, w0, wzr, ror #16" }, + + { 0x2a3e0200, "orn w0, w16, w30, lsl #0" }, + { 0x2a707fdf, "orn wzr, w30, w16, lsr #31" }, + { 0x2aa043f0, "orn w16, wzr, w0, asr #16" }, + { 0x2aff401e, "orn w30, w0, wzr, ror #16" }, + + { 0x729fffe0, "movk w0, #0xffff, lsl #0" }, + { 0x72a0000f, "movk w15, #0x0, lsl #16" }, + { 0x7281fffe, "movk w30, #0xfff, lsl #0" }, + { 0x72a0003f, "movk wzr, #0x1, lsl #16" }, + + { 0x529fffe0, "movz w0, #0xffff, lsl #0" }, + { 0x52a0000f, "movz w15, #0x0, lsl #16" }, + { 0x5281fffe, "movz w30, #0xfff, lsl #0" }, + { 0x52a0003f, "movz wzr, #0x1, lsl #16" }, + + { 0xd29fffe0, "movz x0, #0xffff, lsl #0" }, + { 0xd2a0000f, "movz x15, #0x0, lsl #16" }, + { 0xd2c1fffe, "movz x30, #0xfff, lsl #32" }, + { 0xd2e0003f, "movz xzr, #0x1, lsl #48" }, + + { 0x1a8003e0, "csel w0, wzr, w0, eq" }, + { 0x1a831001, "csel w1, w0, w3, ne" }, + { 0x1a9e2022, "csel w2, w1, w30, cs" }, + { 0x1a8a3083, "csel w3, w4, w10, cc" }, + { 0x1a8b40e4, "csel w4, w7, w11, mi" }, + { 0x1a9b5105, "csel w5, w8, w27, pl" }, + { 0x1a846167, "csel w7, w11, w4, vs" }, + { 0x1a8671c8, "csel w8, w14, w6, vc" }, + { 0x1a878289, "csel w9, w20, w7, hi" }, + { 0x1a8c92aa, "csel w10, w21, w12, ls" }, + { 0x1a8ea2ce, "csel w14, w22, w14, ge" }, + { 0x1a9fb3b2, "csel w18, w29, wzr, lt" }, + { 0x1a9fc3d8, "csel w24, w30, wzr, gt" }, + { 0x1a82d17e, "csel w30, w11, w2, le" }, + { 0x1a81e19f, "csel wzr, w12, w1, al" }, + + { 0x9a8003e0, "csel x0, xzr, x0, eq" }, + { 0x9a831001, "csel x1, x0, x3, ne" }, + { 0x9a9e2022, "csel x2, x1, x30, cs" }, + { 0x9a8a3083, "csel x3, x4, x10, cc" }, + { 0x9a8b40e4, "csel x4, x7, x11, mi" }, + { 0x9a9b5105, "csel x5, x8, x27, pl" }, + { 0x9a846167, "csel x7, x11, x4, vs" }, + { 0x9a8671c8, "csel x8, x14, x6, vc" }, + { 0x9a878289, "csel x9, x20, x7, hi" }, + { 0x9a8c92aa, "csel x10, x21, x12, ls" }, + { 0x9a8ea2ce, "csel x14, x22, x14, ge" }, + { 0x9a9fb3b2, "csel x18, x29, xzr, lt" }, + { 0x9a9fc3d8, "csel x24, x30, xzr, gt" }, + { 0x9a82d17e, "csel x30, x11, x2, le" }, + { 0x9a81e19f, "csel xzr, x12, x1, al" }, + + { 0x5a8003e0, "csinv w0, wzr, w0, eq" }, + { 0x5a831001, "csinv w1, w0, w3, ne" }, + { 0x5a9e2022, "csinv w2, w1, w30, cs" }, + { 0x5a8a3083, "csinv w3, w4, w10, cc" }, + { 0x5a8b40e4, "csinv w4, w7, w11, mi" }, + { 0x5a9b5105, "csinv w5, w8, w27, pl" }, + { 0x5a846167, "csinv w7, w11, w4, vs" }, + { 0x5a8671c8, "csinv w8, w14, w6, vc" }, + { 0x5a878289, "csinv w9, w20, w7, hi" }, + { 0x5a8c92aa, "csinv w10, w21, w12, ls" }, + { 0x5a8ea2ce, "csinv w14, w22, w14, ge" }, + { 0x5a9fb3b2, "csinv w18, w29, wzr, lt" }, + { 0x5a9fc3d8, "csinv w24, w30, wzr, gt" }, + { 0x5a82d17e, "csinv w30, w11, w2, le" }, + { 0x5a81e19f, "csinv wzr, w12, w1, al" }, + + { 0x1b1f3fc0, "madd w0, w30, wzr, w15" }, + { 0x1b0079ef, "madd w15, w15, w0, w30" }, + { 0x1b0f7ffe, "madd w30, wzr, w15, wzr" }, + { 0x1b1e001f, "madd wzr, w0, w30, w0" }, + + { 0x9b3f3fc0, "smaddl x0, w30, wzr, x15" }, + { 0x9b2079ef, "smaddl x15, w15, w0, x30" }, + { 0x9b2f7ffe, "smaddl x30, wzr, w15, xzr" }, + { 0x9b3e001f, "smaddl xzr, w0, w30, x0" }, + + { 0xd65f0000, "ret x0" }, + { 0xd65f01e0, "ret x15" }, + { 0xd65f03c0, "ret x30" }, + { 0xd65f03e0, "ret xzr" }, + + { 0xb87f4be0, "ldr w0, [sp, wzr, uxtw #0]" }, + { 0xb87ed80f, "ldr w15, [x0, w30, sxtw #2]" }, + { 0xb86fc9fe, "ldr w30, [x15, w15, sxtw #0]" }, + { 0xb8605bdf, "ldr wzr, [x30, w0, uxtw #2]" }, + { 0xb87febe0, "ldr w0, [sp, xzr, sxtx #0]" }, + { 0xb87e780f, "ldr w15, [x0, x30, lsl #2]" }, + { 0xb86f69fe, "ldr w30, [x15, x15, lsl #0]" }, + { 0xb860fbdf, "ldr wzr, [x30, x0, sxtx #2]" }, + + { 0xb83f4be0, "str w0, [sp, wzr, uxtw #0]" }, + { 0xb83ed80f, "str w15, [x0, w30, sxtw #2]" }, + { 0xb82fc9fe, "str w30, [x15, w15, sxtw #0]" }, + { 0xb8205bdf, "str wzr, [x30, w0, uxtw #2]" }, + { 0xb83febe0, "str w0, [sp, xzr, sxtx #0]" }, + { 0xb83e780f, "str w15, [x0, x30, lsl #2]" }, + { 0xb82f69fe, "str w30, [x15, x15, lsl #0]" }, + { 0xb820fbdf, "str wzr, [x30, x0, sxtx #2]" }, + + { 0x787f4be0, "ldrh w0, [sp, wzr, uxtw #0]" }, + { 0x787ed80f, "ldrh w15, [x0, w30, sxtw #1]" }, + { 0x786fc9fe, "ldrh w30, [x15, w15, sxtw #0]" }, + { 0x78605bdf, "ldrh wzr, [x30, w0, uxtw #1]" }, + { 0x787febe0, "ldrh w0, [sp, xzr, sxtx #0]" }, + { 0x787e780f, "ldrh w15, [x0, x30, lsl #1]" }, + { 0x786f69fe, "ldrh w30, [x15, x15, lsl #0]" }, + { 0x7860fbdf, "ldrh wzr, [x30, x0, sxtx #1]" }, + + { 0x783f4be0, "strh w0, [sp, wzr, uxtw #0]" }, + { 0x783ed80f, "strh w15, [x0, w30, sxtw #1]" }, + { 0x782fc9fe, "strh w30, [x15, w15, sxtw #0]" }, + { 0x78205bdf, "strh wzr, [x30, w0, uxtw #1]" }, + { 0x783febe0, "strh w0, [sp, xzr, sxtx #0]" }, + { 0x783e780f, "strh w15, [x0, x30, lsl #1]" }, + { 0x782f69fe, "strh w30, [x15, x15, lsl #0]" }, + { 0x7820fbdf, "strh wzr, [x30, x0, sxtx #1]" }, + + { 0x387f5be0, "ldrb w0, [sp, wzr, uxtw #0]" }, + { 0x387ec80f, "ldrb w15, [x0, w30, sxtw ]" }, + { 0x386fd9fe, "ldrb w30, [x15, w15, sxtw #0]" }, + { 0x38604bdf, "ldrb wzr, [x30, w0, uxtw ]" }, + { 0x387ffbe0, "ldrb w0, [sp, xzr, sxtx #0]" }, + { 0x387e780f, "ldrb w15, [x0, x30, lsl #0]" }, + { 0x386f79fe, "ldrb w30, [x15, x15, lsl #0]" }, + { 0x3860ebdf, "ldrb wzr, [x30, x0, sxtx ]" }, + + { 0x383f5be0, "strb w0, [sp, wzr, uxtw #0]" }, + { 0x383ec80f, "strb w15, [x0, w30, sxtw ]" }, + { 0x382fd9fe, "strb w30, [x15, w15, sxtw #0]" }, + { 0x38204bdf, "strb wzr, [x30, w0, uxtw ]" }, + { 0x383ffbe0, "strb w0, [sp, xzr, sxtx #0]" }, + { 0x383e780f, "strb w15, [x0, x30, lsl #0]" }, + { 0x382f79fe, "strb w30, [x15, x15, lsl #0]" }, + { 0x3820ebdf, "strb wzr, [x30, x0, sxtx ]" }, + + { 0xf87f4be0, "ldr x0, [sp, wzr, uxtw #0]" }, + { 0xf87ed80f, "ldr x15, [x0, w30, sxtw #3]" }, + { 0xf86fc9fe, "ldr x30, [x15, w15, sxtw #0]" }, + { 0xf8605bdf, "ldr xzr, [x30, w0, uxtw #3]" }, + { 0xf87febe0, "ldr x0, [sp, xzr, sxtx #0]" }, + { 0xf87e780f, "ldr x15, [x0, x30, lsl #3]" }, + { 0xf86f69fe, "ldr x30, [x15, x15, lsl #0]" }, + { 0xf860fbdf, "ldr xzr, [x30, x0, sxtx #3]" }, + + { 0xf83f4be0, "str x0, [sp, wzr, uxtw #0]" }, + { 0xf83ed80f, "str x15, [x0, w30, sxtw #3]" }, + { 0xf82fc9fe, "str x30, [x15, w15, sxtw #0]" }, + { 0xf8205bdf, "str xzr, [x30, w0, uxtw #3]" }, + { 0xf83febe0, "str x0, [sp, xzr, sxtx #0]" }, + { 0xf83e780f, "str x15, [x0, x30, lsl #3]" }, + { 0xf82f69fe, "str x30, [x15, x15, lsl #0]" }, + { 0xf820fbdf, "str xzr, [x30, x0, sxtx #3]" }, + + { 0xb85007e0, "ldr w0, [sp], #-256" }, + { 0xb840040f, "ldr w15, [x0], #0" }, + { 0xb84015fe, "ldr w30, [x15], #1" }, + { 0xb84ff7df, "ldr wzr, [x30], #255" }, + { 0xb8100fe0, "str w0, [sp, #-256]!" }, + { 0xb8000c0f, "str w15, [x0, #0]!" }, + { 0xb8001dfe, "str w30, [x15, #1]!" }, + { 0xb80fffdf, "str wzr, [x30, #255]!" }, + + { 0x13017be0, "sbfm w0, wzr, #1, #30" }, + { 0x131e7fcf, "sbfm w15, w30, #30, #31" }, + { 0x131f01fe, "sbfm w30, w15, #31, #0" }, + { 0x1300041f, "sbfm wzr, w0, #0, #1" }, + + { 0x53017be0, "ubfm w0, wzr, #1, #30" }, + { 0x531e7fcf, "ubfm w15, w30, #30, #31" }, + { 0x531f01fe, "ubfm w30, w15, #31, #0" }, + { 0x5300041f, "ubfm wzr, w0, #0, #1" }, + { 0xd3417fe0, "ubfm x0, xzr, #1, #31" }, + { 0xd35fffcf, "ubfm x15, x30, #31, #63" }, + { 0xd35f01fe, "ubfm x30, x15, #31, #0" }, + { 0xd340041f, "ubfm xzr, x0, #0, #1" }, + + { 0x139e7be0, "extr w0, wzr, w30, #30" }, + { 0x138f7fcf, "extr w15, w30, w15, #31" }, + { 0x138001fe, "extr w30, w15, w0, #0" }, + { 0x139f041f, "extr wzr, w0, wzr, #1" }, + + { 0x54000020, "b.eq #.+4" }, + { 0x54000201, "b.ne #.+64" }, + { 0x54000802, "b.cs #.+256" }, + { 0x54002003, "b.cc #.+1024" }, + { 0x54008004, "b.mi #.+4096" }, + { 0x54ffffe5, "b.pl #.-4" }, + { 0x54ffff06, "b.vs #.-32" }, + { 0x54fffc07, "b.vc #.-128" }, + { 0x54fff008, "b.hi #.-512" }, + { 0x54000049, "b.ls #.+8" }, + { 0x5400006a, "b.ge #.+12" }, + { 0x5400008b, "b.lt #.+16" }, + { 0x54ffffcc, "b.gt #.-8" }, + { 0x54ffffad, "b.le #.-12" }, + { 0x54ffff8e, "b.al #.-16" }, + + { 0x8b2001e0, "add x0, x15, w0, uxtb #0" }, + { 0x8b2f27cf, "add x15, x30, w15, uxth #1" }, + { 0x8b3e4bfe, "add x30, sp, w30, uxtw #2" }, + { 0x8b3f6c1f, "add sp, x0, xzr, uxtx #3" }, + { 0x8b2091e0, "add x0, x15, w0, sxtb #4" }, + { 0x8b2fa3cf, "add x15, x30, w15, sxth #0" }, + { 0x8b3ec7fe, "add x30, sp, w30, sxtw #1" }, + { 0x8b3fe81f, "add sp, x0, xzr, sxtx #2" }, + + { 0xcb2001e0, "sub x0, x15, w0, uxtb #0" }, + { 0xcb2f27cf, "sub x15, x30, w15, uxth #1" }, + { 0xcb3e4bfe, "sub x30, sp, w30, uxtw #2" }, + { 0xcb3f6c1f, "sub sp, x0, xzr, uxtx #3" }, + { 0xcb2091e0, "sub x0, x15, w0, sxtb #4" }, + { 0xcb2fa3cf, "sub x15, x30, w15, sxth #0" }, + { 0xcb3ec7fe, "sub x30, sp, w30, sxtw #1" }, + { 0xcb3fe81f, "sub sp, x0, xzr, sxtx #2" } +}; + +int main() +{ + char instr[256]; + uint32_t failed = 0; + for(uint32_t i = 0; i < sizeof(test_table)/sizeof(test_table_entry_t); ++i) + { + test_table_entry_t *test; + test = &test_table[i]; + arm64_disassemble(test->code, instr); + if(strcmp(instr, test->instr) != 0) + { + printf("Test Failed \n" + "Code : 0x%0x\n" + "Expected : %s\n" + "Actual : %s\n", test->code, test->instr, instr); + failed++; + } + } + if(failed == 0) + { + printf("All tests PASSED\n"); + return 0; + } + else + { + printf("%d tests FAILED\n", failed); + return -1; + } +} diff --git a/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk b/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk new file mode 100644 index 0000000..1cce1bd --- /dev/null +++ b/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk @@ -0,0 +1,16 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + t32cb16blend_test.c \ + ../../../arch-arm64/t32cb16blend.S + +LOCAL_SHARED_LIBRARIES := + +LOCAL_C_INCLUDES := + +LOCAL_MODULE:= test-pixelflinger-arm64-t32cb16blend + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) diff --git a/libpixelflinger/tests/arch-arm64/t32cb16blend/t32cb16blend_test.c b/libpixelflinger/tests/arch-arm64/t32cb16blend/t32cb16blend_test.c new file mode 100644 index 0000000..afb36fb --- /dev/null +++ b/libpixelflinger/tests/arch-arm64/t32cb16blend/t32cb16blend_test.c @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> + +#define ARGB_8888_MAX 0xFFFFFFFF +#define ARGB_8888_MIN 0x00000000 +#define RGB_565_MAX 0xFFFF +#define RGB_565_MIN 0x0000 + +struct test_t +{ + char name[256]; + uint32_t src_color; + uint16_t dst_color; + size_t count; +}; + +struct test_t tests[] = +{ + {"Count 0", 0, 0, 0}, + {"Count 1, Src=Max, Dst=Min", ARGB_8888_MAX, RGB_565_MIN, 1}, + {"Count 2, Src=Min, Dst=Max", ARGB_8888_MIN, RGB_565_MAX, 2}, + {"Count 3, Src=Max, Dst=Max", ARGB_8888_MAX, RGB_565_MAX, 3}, + {"Count 4, Src=Min, Dst=Min", ARGB_8888_MAX, RGB_565_MAX, 4}, + {"Count 1, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 1}, + {"Count 2, Src=Rand, Dst=Rand", 0xABCDEF12, 0x2345, 2}, + {"Count 3, Src=Rand, Dst=Rand", 0x11111111, 0xEDFE, 3}, + {"Count 4, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 4}, + {"Count 5, Src=Rand, Dst=Rand", 0xEFEFFEFE, 0xFACC, 5}, + {"Count 10, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 10} + +}; + +void scanline_t32cb16blend_arm64(uint16_t*, uint32_t*, size_t); +void scanline_t32cb16blend_c(uint16_t * dst, uint32_t* src, size_t count) +{ + while (count--) + { + uint16_t d = *dst; + uint32_t s = *src++; + int dstR = (d>>11)&0x1f; + int dstG = (d>>5)&0x3f; + int dstB = (d)&0x1f; + int srcR = (s >> ( 3))&0x1F; + int srcG = (s >> ( 8+2))&0x3F; + int srcB = (s >> (16+3))&0x1F; + int srcAlpha = (s>>24) & 0xFF; + + + int f = 0x100 - (srcAlpha + ((srcAlpha>>7) & 0x1)); + srcR += (f*dstR)>>8; + srcG += (f*dstG)>>8; + srcB += (f*dstB)>>8; + srcR = srcR > 0x1F? 0x1F: srcR; + srcG = srcG > 0x3F? 0x3F: srcG; + srcB = srcB > 0x1F? 0x1F: srcB; + *dst++ = (uint16_t)((srcR<<11)|(srcG<<5)|srcB); + } +} + +void scanline_t32cb16blend_test() +{ + uint16_t dst_c[16], dst_asm[16]; + uint32_t src[16]; + uint32_t i; + uint32_t j; + + for(i = 0; i < sizeof(tests)/sizeof(struct test_t); ++i) + { + struct test_t test = tests[i]; + + printf("Testing - %s:",test.name); + + memset(dst_c, 0, sizeof(dst_c)); + memset(dst_asm, 0, sizeof(dst_asm)); + + for(j = 0; j < test.count; ++j) + { + dst_c[j] = test.dst_color; + dst_asm[j] = test.dst_color; + src[j] = test.src_color; + } + + scanline_t32cb16blend_c(dst_c,src,test.count); + scanline_t32cb16blend_arm64(dst_asm,src,test.count); + + + if(memcmp(dst_c, dst_asm, sizeof(dst_c)) == 0) + printf("Passed\n"); + else + printf("Failed\n"); + + for(j = 0; j < test.count; ++j) + { + printf("dst_c[%d] = %x, dst_asm[%d] = %x \n", j, dst_c[j], j, dst_asm[j]); + } + } +} + +int main() +{ + scanline_t32cb16blend_test(); + return 0; +} diff --git a/libpixelflinger/tests/codegen/codegen.cpp b/libpixelflinger/tests/codegen/codegen.cpp index 3d5a040..e9f6c61 100644 --- a/libpixelflinger/tests/codegen/codegen.cpp +++ b/libpixelflinger/tests/codegen/codegen.cpp @@ -10,8 +10,9 @@ #include "codeflinger/GGLAssembler.h" #include "codeflinger/ARMAssembler.h" #include "codeflinger/MIPSAssembler.h" +#include "codeflinger/Arm64Assembler.h" -#if defined(__arm__) || defined(__mips__) +#if defined(__arm__) || defined(__mips__) || defined(__aarch64__) # define ANDROID_ARM_CODEGEN 1 #else # define ANDROID_ARM_CODEGEN 0 @@ -19,6 +20,8 @@ #if defined (__mips__) #define ASSEMBLY_SCRATCH_SIZE 4096 +#elif defined(__aarch64__) +#define ASSEMBLY_SCRATCH_SIZE 8192 #else #define ASSEMBLY_SCRATCH_SIZE 2048 #endif @@ -53,13 +56,17 @@ static void ggl_test_codegen(uint32_t n, uint32_t p, uint32_t t0, uint32_t t1) GGLAssembler assembler( new ArmToMipsAssembler(a) ); #endif +#if defined(__aarch64__) + GGLAssembler assembler( new ArmToArm64Assembler(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 or MIPS\n"); + printf("This test runs only on ARM, Arm64 or MIPS\n"); #endif } diff --git a/libpixelflinger/tests/gglmul/Android.mk b/libpixelflinger/tests/gglmul/Android.mk new file mode 100644 index 0000000..64f88b7 --- /dev/null +++ b/libpixelflinger/tests/gglmul/Android.mk @@ -0,0 +1,16 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + gglmul_test.cpp + +LOCAL_SHARED_LIBRARIES := + +LOCAL_C_INCLUDES := \ + system/core/libpixelflinger + +LOCAL_MODULE:= test-pixelflinger-gglmul + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) diff --git a/libpixelflinger/tests/gglmul/gglmul_test.cpp b/libpixelflinger/tests/gglmul/gglmul_test.cpp new file mode 100644 index 0000000..073368e --- /dev/null +++ b/libpixelflinger/tests/gglmul/gglmul_test.cpp @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdint.h> + +#include "private/pixelflinger/ggl_fixed.h" + +// gglClampx() tests +struct gglClampx_test_t +{ + GGLfixed input; + GGLfixed output; +}; + +gglClampx_test_t gglClampx_tests[] = +{ + {FIXED_ONE + 1, FIXED_ONE}, + {FIXED_ONE, FIXED_ONE}, + {FIXED_ONE - 1, FIXED_ONE - 1}, + {1, 1}, + {0, 0}, + {FIXED_MIN,0} +}; + +void gglClampx_test() +{ + uint32_t i; + + printf("Testing gglClampx\n"); + for(i = 0; i < sizeof(gglClampx_tests)/sizeof(gglClampx_test_t); ++i) + { + gglClampx_test_t *test = &gglClampx_tests[i]; + printf("Test input=0x%08x output=0x%08x :", + test->input, test->output); + if(gglClampx(test->input) == test->output) + printf("Passed\n"); + else + printf("Failed\n"); + } +} + +// gglClz() tests +struct gglClz_test_t +{ + GGLfixed input; + GGLfixed output; +}; + +gglClz_test_t gglClz_tests[] = +{ + {0, 32}, + {1, 31}, + {-1,0} +}; + +void gglClz_test() +{ + uint32_t i; + + printf("Testing gglClz\n"); + for(i = 0; i < sizeof(gglClz_tests)/sizeof(gglClz_test_t); ++i) + { + gglClz_test_t *test = &gglClz_tests[i]; + printf("Test input=0x%08x output=%2d :", test->input, test->output); + if(gglClz(test->input) == test->output) + printf("Passed\n"); + else + printf("Failed\n"); + } +} + +// gglMulx() tests +struct gglMulx_test_t +{ + GGLfixed x; + GGLfixed y; + int shift; +}; + +gglMulx_test_t gglMulx_tests[] = +{ + {1,1,1}, + {0,1,1}, + {FIXED_ONE,FIXED_ONE,16}, + {FIXED_MIN,FIXED_MAX,16}, + {FIXED_MAX,FIXED_MAX,16}, + {FIXED_MIN,FIXED_MIN,16}, + {FIXED_HALF,FIXED_ONE,16}, + {FIXED_MAX,FIXED_MAX,31}, + {FIXED_ONE,FIXED_MAX,31} +}; + +void gglMulx_test() +{ + uint32_t i; + GGLfixed actual, expected; + + printf("Testing gglMulx\n"); + for(i = 0; i < sizeof(gglMulx_tests)/sizeof(gglMulx_test_t); ++i) + { + gglMulx_test_t *test = &gglMulx_tests[i]; + printf("Test x=0x%08x y=0x%08x shift=%2d :", + test->x, test->y, test->shift); + actual = gglMulx(test->x, test->y, test->shift); + expected = + ((int64_t)test->x * test->y + (1 << (test->shift-1))) >> test->shift; + if(actual == expected) + printf(" Passed\n"); + else + printf(" Failed Actual(0x%08x) Expected(0x%08x)\n", + actual, expected); + } +} +// gglMulAddx() tests +struct gglMulAddx_test_t +{ + GGLfixed x; + GGLfixed y; + int shift; + GGLfixed a; +}; + +gglMulAddx_test_t gglMulAddx_tests[] = +{ + {1,2,1,1}, + {0,1,1,1}, + {FIXED_ONE,FIXED_ONE,16, 0}, + {FIXED_MIN,FIXED_MAX,16, FIXED_HALF}, + {FIXED_MAX,FIXED_MAX,16, FIXED_MIN}, + {FIXED_MIN,FIXED_MIN,16, FIXED_MAX}, + {FIXED_HALF,FIXED_ONE,16,FIXED_ONE}, + {FIXED_MAX,FIXED_MAX,31, FIXED_HALF}, + {FIXED_ONE,FIXED_MAX,31, FIXED_HALF} +}; + +void gglMulAddx_test() +{ + uint32_t i; + GGLfixed actual, expected; + + printf("Testing gglMulAddx\n"); + for(i = 0; i < sizeof(gglMulAddx_tests)/sizeof(gglMulAddx_test_t); ++i) + { + gglMulAddx_test_t *test = &gglMulAddx_tests[i]; + printf("Test x=0x%08x y=0x%08x shift=%2d a=0x%08x :", + test->x, test->y, test->shift, test->a); + actual = gglMulAddx(test->x, test->y,test->a, test->shift); + expected = (((int64_t)test->x * test->y) >> test->shift) + test->a; + + if(actual == expected) + printf(" Passed\n"); + else + printf(" Failed Actual(0x%08x) Expected(0x%08x)\n", + actual, expected); + } +} +// gglMulSubx() tests +struct gglMulSubx_test_t +{ + GGLfixed x; + GGLfixed y; + int shift; + GGLfixed a; +}; + +gglMulSubx_test_t gglMulSubx_tests[] = +{ + {1,2,1,1}, + {0,1,1,1}, + {FIXED_ONE,FIXED_ONE,16, 0}, + {FIXED_MIN,FIXED_MAX,16, FIXED_HALF}, + {FIXED_MAX,FIXED_MAX,16, FIXED_MIN}, + {FIXED_MIN,FIXED_MIN,16, FIXED_MAX}, + {FIXED_HALF,FIXED_ONE,16,FIXED_ONE}, + {FIXED_MAX,FIXED_MAX,31, FIXED_HALF}, + {FIXED_ONE,FIXED_MAX,31, FIXED_HALF} +}; + +void gglMulSubx_test() +{ + uint32_t i; + GGLfixed actual, expected; + + printf("Testing gglMulSubx\n"); + for(i = 0; i < sizeof(gglMulSubx_tests)/sizeof(gglMulSubx_test_t); ++i) + { + gglMulSubx_test_t *test = &gglMulSubx_tests[i]; + printf("Test x=0x%08x y=0x%08x shift=%2d a=0x%08x :", + test->x, test->y, test->shift, test->a); + actual = gglMulSubx(test->x, test->y, test->a, test->shift); + expected = (((int64_t)test->x * test->y) >> test->shift) - test->a; + + if(actual == expected) + printf(" Passed\n"); + else + printf(" Failed Actual(0x%08x) Expected(0x%08x)\n", + actual, expected); + } +} + +// gglMulii() tests + +struct gglMulii_test_t +{ + int32_t x; + int32_t y; +}; + +gglMulii_test_t gglMulii_tests[] = +{ + {1,INT32_MIN}, + {1,INT32_MAX}, + {0,INT32_MIN}, + {0,INT32_MAX}, + {INT32_MIN, INT32_MAX}, + {INT32_MAX, INT32_MIN}, + {INT32_MIN, INT32_MIN}, + {INT32_MAX, INT32_MAX} +}; + +void gglMulii_test() +{ + uint32_t i; + int64_t actual, expected; + + printf("Testing gglMulii\n"); + for(i = 0; i < sizeof(gglMulii_tests)/sizeof(gglMulii_test_t); ++i) + { + gglMulii_test_t *test = &gglMulii_tests[i]; + printf("Test x=0x%08x y=0x%08x :", test->x, test->y); + actual = gglMulii(test->x, test->y); + expected = ((int64_t)test->x * test->y); + + if(actual == expected) + printf(" Passed\n"); + else + printf(" Failed Actual(%ld) Expected(%ld)\n", + actual, expected); + } +} + +int main(int argc, char** argv) +{ + gglClampx_test(); + gglClz_test(); + gglMulx_test(); + gglMulAddx_test(); + gglMulSubx_test(); + gglMulii_test(); + return 0; +} diff --git a/libsparse/backed_block.c b/libsparse/backed_block.c index dfb217b..3e72b57 100644 --- a/libsparse/backed_block.c +++ b/libsparse/backed_block.c @@ -370,7 +370,7 @@ int backed_block_split(struct backed_block_list *bbl, struct backed_block *bb, } new_bb = malloc(sizeof(struct backed_block)); - if (bb == NULL) { + if (new_bb == NULL) { return -ENOMEM; } diff --git a/libsparse/output_file.c b/libsparse/output_file.c index 5014e4a..e63c4a9 100644 --- a/libsparse/output_file.c +++ b/libsparse/output_file.c @@ -18,6 +18,7 @@ #define _LARGEFILE64_SOURCE 1 #include <fcntl.h> +#include <inttypes.h> #include <limits.h> #include <stdbool.h> #include <stddef.h> @@ -46,15 +47,6 @@ #define off64_t off_t #endif -#ifdef __BIONIC__ -extern void* __mmap2(void *, size_t, int, int, int, off_t); -static inline void *mmap64(void *addr, size_t length, int prot, int flags, - int fd, off64_t offset) -{ - return __mmap2(addr, length, prot, flags, fd, offset >> 12); -} -#endif - #define min(a, b) \ ({ typeof(a) _a = (a); typeof(b) _b = (b); (_a < _b) ? _a : _b; }) @@ -351,7 +343,7 @@ static int write_sparse_skip_chunk(struct output_file *out, int64_t skip_len) int ret, chunk; if (skip_len % out->block_size) { - error("don't care size %llu is not a multiple of the block size %u", + error("don't care size %"PRIi64" is not a multiple of the block size %u", skip_len, out->block_size); return -1; } @@ -731,10 +723,12 @@ int write_fd_chunk(struct output_file *out, unsigned int len, } pos = lseek64(fd, offset, SEEK_SET); if (pos < 0) { + free(data); return -errno; } ret = read_all(fd, data, len); if (ret < 0) { + free(data); return ret; } ptr = data; diff --git a/libsync/Android.mk b/libsync/Android.mk index 73de069..626b762 100644 --- a/libsync/Android.mk +++ b/libsync/Android.mk @@ -5,6 +5,8 @@ LOCAL_SRC_FILES := sync.c LOCAL_MODULE := libsync LOCAL_MODULE_TAGS := optional LOCAL_SHARED_LIBRARIES := liblog +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include include $(BUILD_SHARED_LIBRARY) include $(CLEAR_VARS) @@ -12,4 +14,5 @@ LOCAL_SRC_FILES := sync.c sync_test.c LOCAL_MODULE := sync_test LOCAL_MODULE_TAGS := optional tests LOCAL_SHARED_LIBRARIES := liblog +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include include $(BUILD_EXECUTABLE) diff --git a/include/sync/sync.h b/libsync/include/sync/sync.h index 2e5d82f..2e5d82f 100644 --- a/include/sync/sync.h +++ b/libsync/include/sync/sync.h diff --git a/include/sync/sw_sync.h b/libsync/sw_sync.h index 3bf4110..fda1c4c 100644 --- a/include/sync/sw_sync.h +++ b/libsync/sw_sync.h @@ -19,8 +19,6 @@ #ifndef __SYS_CORE_SW_SYNC_H #define __SYS_CORE_SW_SYNC_H -#include "sync.h" - __BEGIN_DECLS /* diff --git a/libsync/sync_test.c b/libsync/sync_test.c index 386747a..ee9ea3c 100644 --- a/libsync/sync_test.c +++ b/libsync/sync_test.c @@ -23,6 +23,7 @@ #include <unistd.h> #include <sync/sync.h> +#include "sw_sync.h" pthread_mutex_t printf_mutex = PTHREAD_MUTEX_INITIALIZER; diff --git a/libsysutils/Android.mk b/libsysutils/Android.mk index 1d396b2..1451b0d 100644 --- a/libsysutils/Android.mk +++ b/libsysutils/Android.mk @@ -16,7 +16,7 @@ LOCAL_SRC_FILES:= \ LOCAL_MODULE:= libsysutils -LOCAL_C_INCLUDES := $(KERNEL_HEADERS) +LOCAL_C_INCLUDES := LOCAL_CFLAGS := diff --git a/libsysutils/src/FrameworkCommand.cpp b/libsysutils/src/FrameworkCommand.cpp index 038d87e..0b95a81 100644 --- a/libsysutils/src/FrameworkCommand.cpp +++ b/libsysutils/src/FrameworkCommand.cpp @@ -21,11 +21,14 @@ #include <sysutils/FrameworkCommand.h> +#define UNUSED __attribute__((unused)) + FrameworkCommand::FrameworkCommand(const char *cmd) { mCommand = cmd; } -int FrameworkCommand::runCommand(SocketClient *c, int argc, char **argv) { +int FrameworkCommand::runCommand(SocketClient *c UNUSED, int argc UNUSED, + char **argv UNUSED) { SLOGW("Command %s has no run handler!", getCommand()); errno = ENOSYS; return -1; diff --git a/libsysutils/src/FrameworkListener.cpp b/libsysutils/src/FrameworkListener.cpp index 02a401d..a5ffda2 100644 --- a/libsysutils/src/FrameworkListener.cpp +++ b/libsysutils/src/FrameworkListener.cpp @@ -27,6 +27,8 @@ static const int CMD_BUF_SIZE = 1024; +#define UNUSED __attribute__((unused)) + FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) : SocketListener(socketName, true, withSeq) { init(socketName, withSeq); @@ -37,7 +39,7 @@ FrameworkListener::FrameworkListener(const char *socketName) : init(socketName, false); } -void FrameworkListener::init(const char *socketName, bool withSeq) { +void FrameworkListener::init(const char *socketName UNUSED, bool withSeq) { mCommands = new FrameworkCommandCollection(); errorRate = 0; mCommandCount = 0; diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp index aae2ae7..34f2016 100644 --- a/libsysutils/src/NetlinkEvent.cpp +++ b/libsysutils/src/NetlinkEvent.cpp @@ -24,6 +24,7 @@ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> +#include <netinet/icmp6.h> #include <arpa/inet.h> #include <net/if.h> @@ -44,6 +45,7 @@ const int NetlinkEvent::NlActionLinkUp = 4; const int NetlinkEvent::NlActionLinkDown = 5; const int NetlinkEvent::NlActionAddressUpdated = 6; const int NetlinkEvent::NlActionAddressRemoved = 7; +const int NetlinkEvent::NlActionRdnss = 8; NetlinkEvent::NetlinkEvent() { mAction = NlActionUnknown; @@ -76,7 +78,7 @@ void NetlinkEvent::dump() { } /* - * Decode a RTM_NEWADDR or RTM_DELADDR message. + * Parse a RTM_NEWADDR or RTM_DELADDR message. */ bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, int rtasize) { @@ -172,13 +174,112 @@ bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, } /* - * Parse an binary message from a NETLINK_ROUTE netlink socket. +<<<<<<< HEAD + * Parse a RTM_NEWNDUSEROPT message. + */ +bool NetlinkEvent::parseNdUserOptMessage(struct nduseroptmsg *msg, int len) { + // Check the length is valid. + if (msg->nduseropt_opts_len > len) { + SLOGE("RTM_NEWNDUSEROPT invalid length %d > %d\n", + msg->nduseropt_opts_len, len); + return false; + } + len = msg->nduseropt_opts_len; + + // Check address family and packet type. + if (msg->nduseropt_family != AF_INET6) { + SLOGE("RTM_NEWNDUSEROPT message for unknown family %d\n", + msg->nduseropt_family); + return false; + } + + if (msg->nduseropt_icmp_type != ND_ROUTER_ADVERT || + msg->nduseropt_icmp_code != 0) { + SLOGE("RTM_NEWNDUSEROPT message for unknown ICMPv6 type/code %d/%d\n", + msg->nduseropt_icmp_type, msg->nduseropt_icmp_code); + return false; + } + + // Find the interface name. + char ifname[IFNAMSIZ + 1]; + if (!if_indextoname(msg->nduseropt_ifindex, ifname)) { + SLOGE("RTM_NEWNDUSEROPT on unknown ifindex %d\n", + msg->nduseropt_ifindex); + return false; + } + + // The kernel sends a separate netlink message for each ND option in the RA. + // So only parse the first ND option in the message. + struct nd_opt_hdr *opthdr = (struct nd_opt_hdr *) (msg + 1); + + // The length is in multiples of 8 octets. + uint16_t optlen = opthdr->nd_opt_len; + if (optlen * 8 > len) { + SLOGE("Invalid option length %d > %d for ND option %d\n", + optlen * 8, len, opthdr->nd_opt_type); + return false; + } + + if (opthdr->nd_opt_type == ND_OPT_RDNSS) { + // DNS Servers (RFC 6106). + // Each address takes up 2*8 octets, and the header takes up 8 octets. + // So for a valid option with one or more addresses, optlen must be + // odd and greater than 1. + if ((optlen < 3) || !(optlen & 0x1)) { + SLOGE("Invalid optlen %d for RDNSS option\n", optlen); + return false; + } + int numaddrs = (optlen - 1) / 2; + + // Find the lifetime. + struct nd_opt_rdnss *rndss_opt = (struct nd_opt_rdnss *) opthdr; + uint32_t lifetime = ntohl(rndss_opt->nd_opt_rdnss_lifetime); + + // Construct "SERVERS=<comma-separated string of DNS addresses>". + // Reserve (INET6_ADDRSTRLEN + 1) chars for each address: all but the + // the last address are followed by ','; the last is followed by '\0'. + static const char kServerTag[] = "SERVERS="; + static const int kTagLength = sizeof(kServerTag) - 1; + int bufsize = kTagLength + numaddrs * (INET6_ADDRSTRLEN + 1); + char *buf = (char *) malloc(bufsize); + if (!buf) { + SLOGE("RDNSS option: out of memory\n"); + return false; + } + strcpy(buf, kServerTag); + int pos = kTagLength; + + struct in6_addr *addrs = (struct in6_addr *) (rndss_opt + 1); + for (int i = 0; i < numaddrs; i++) { + if (i > 0) { + buf[pos++] = ','; + } + inet_ntop(AF_INET6, addrs + i, buf + pos, bufsize - pos); + pos += strlen(buf + pos); + } + buf[pos] = '\0'; + + mAction = NlActionRdnss; + mSubsystem = strdup("net"); + asprintf(&mParams[0], "INTERFACE=%s", ifname); + asprintf(&mParams[1], "LIFETIME=%u", lifetime); + mParams[2] = buf; + } else { + SLOGD("Unknown ND option type %d\n", opthdr->nd_opt_type); + return false; + } + + return true; +} + +/* + * Parse a binary message from a NETLINK_ROUTE netlink socket. */ bool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) { const struct nlmsghdr *nh; for (nh = (struct nlmsghdr *) buffer; - NLMSG_OK(nh, size) && (nh->nlmsg_type != NLMSG_DONE); + NLMSG_OK(nh, (unsigned) size) && (nh->nlmsg_type != NLMSG_DONE); nh = NLMSG_NEXT(nh, size)) { if (nh->nlmsg_type == RTM_NEWLINK) { @@ -245,8 +346,25 @@ bool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) { if (!parseIfAddrMessage(nh->nlmsg_type, ifa, rtasize)) { continue; } + + } else if (nh->nlmsg_type == RTM_NEWNDUSEROPT) { + int len = nh->nlmsg_len - sizeof(*nh); + struct nduseroptmsg *ndmsg = (struct nduseroptmsg *) NLMSG_DATA(nh); + + if (sizeof(*ndmsg) > (size_t) len) { + SLOGE("Got a short RTM_NEWNDUSEROPT message\n"); + continue; + } + + size_t optsize = NLMSG_PAYLOAD(nh, sizeof(*ndmsg)); + if (!parseNdUserOptMessage(ndmsg, optsize)) { + continue; + } + + } else { - SLOGD("Unexpected netlink message. type=0x%x\n", nh->nlmsg_type); + SLOGD("Unexpected netlink message. type=0x%x\n", + nh->nlmsg_type); } } diff --git a/libsysutils/src/SocketClient.cpp b/libsysutils/src/SocketClient.cpp index ae0e077..3625d93 100644 --- a/libsysutils/src/SocketClient.cpp +++ b/libsysutils/src/SocketClient.cpp @@ -71,7 +71,7 @@ int SocketClient::sendMsg(int code, const char *msg, bool addErrno, bool useCmdN ret = asprintf(&buf, "%d %s", code, msg); } } - /* Send the zero-terminated message */ + // Send the zero-terminated message if (ret != -1) { ret = sendMsg(buf); free(buf); @@ -79,22 +79,25 @@ int SocketClient::sendMsg(int code, const char *msg, bool addErrno, bool useCmdN return ret; } -/** send 3-digit code, null, binary-length, binary data */ +// send 3-digit code, null, binary-length, binary data int SocketClient::sendBinaryMsg(int code, const void *data, int len) { - /* 4 bytes for the code & null + 4 bytes for the len */ + // 4 bytes for the code & null + 4 bytes for the len char buf[8]; - /* Write the code */ + // Write the code snprintf(buf, 4, "%.3d", code); - /* Write the len */ + // Write the len uint32_t tmp = htonl(len); memcpy(buf + 4, &tmp, sizeof(uint32_t)); + struct iovec vec[2]; + vec[0].iov_base = (void *) buf; + vec[0].iov_len = sizeof(buf); + vec[1].iov_base = (void *) data; + vec[1].iov_len = len; + pthread_mutex_lock(&mWriteMutex); - int result = sendDataLocked(buf, sizeof(buf)); - if (result == 0 && len > 0) { - result = sendDataLocked(data, len); - } + int result = sendDataLockedv(vec, (len > 0) ? 2 : 1); pthread_mutex_unlock(&mWriteMutex); return result; @@ -147,33 +150,51 @@ int SocketClient::sendMsg(const char *msg) { } int SocketClient::sendData(const void *data, int len) { + struct iovec vec[1]; + vec[0].iov_base = (void *) data; + vec[0].iov_len = len; pthread_mutex_lock(&mWriteMutex); - int rc = sendDataLocked(data, len); + int rc = sendDataLockedv(vec, 1); pthread_mutex_unlock(&mWriteMutex); return rc; } -int SocketClient::sendDataLocked(const void *data, int len) { - int rc = 0; - const char *p = (const char*) data; - int brtw = len; +int SocketClient::sendDatav(struct iovec *iov, int iovcnt) { + pthread_mutex_lock(&mWriteMutex); + int rc = sendDataLockedv(iov, iovcnt); + pthread_mutex_unlock(&mWriteMutex); + + return rc; +} + +int SocketClient::sendDataLockedv(struct iovec *iov, int iovcnt) { if (mSocket < 0) { errno = EHOSTUNREACH; return -1; } - if (len == 0) { + if (iovcnt <= 0) { return 0; } - while (brtw > 0) { - rc = send(mSocket, p, brtw, MSG_NOSIGNAL); + int current = 0; + + for (;;) { + ssize_t rc = writev(mSocket, iov + current, iovcnt - current); if (rc > 0) { - p += rc; - brtw -= rc; + size_t written = rc; + while ((current < iovcnt) && (written >= iov[current].iov_len)) { + written -= iov[current].iov_len; + current++; + } + if (current == iovcnt) { + break; + } + iov[current].iov_base = (char *)iov[current].iov_base + written; + iov[current].iov_len -= written; continue; } diff --git a/libsysutils/src/SocketListener.cpp b/libsysutils/src/SocketListener.cpp index 0361641..5c75206 100644 --- a/libsysutils/src/SocketListener.cpp +++ b/libsysutils/src/SocketListener.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2008-2014 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. @@ -29,7 +29,8 @@ #include <sysutils/SocketListener.h> #include <sysutils/SocketClient.h> -#define LOG_NDEBUG 0 +#define CtrlPipe_Shutdown 0 +#define CtrlPipe_Wakeup 1 SocketListener::SocketListener(const char *socketName, bool listen) { init(socketName, -1, listen, false); @@ -103,7 +104,7 @@ int SocketListener::startListener() { } int SocketListener::stopListener() { - char c = 0; + char c = CtrlPipe_Shutdown; int rc; rc = TEMP_FAILURE_RETRY(write(mCtrlPipe[1], &c, 1)); @@ -145,7 +146,7 @@ void *SocketListener::threadStart(void *obj) { void SocketListener::runListener() { - SocketClientCollection *pendingList = new SocketClientCollection(); + SocketClientCollection pendingList; while(1) { SocketClientCollection::iterator it; @@ -166,10 +167,12 @@ void SocketListener::runListener() { pthread_mutex_lock(&mClientsLock); for (it = mClients->begin(); it != mClients->end(); ++it) { + // NB: calling out to an other object with mClientsLock held (safe) int fd = (*it)->getSocket(); FD_SET(fd, &read_fds); - if (fd > max) + if (fd > max) { max = fd; + } } pthread_mutex_unlock(&mClientsLock); SLOGV("mListen=%d, max=%d, mSocketName=%s", mListen, max, mSocketName); @@ -182,8 +185,14 @@ void SocketListener::runListener() { } else if (!rc) continue; - if (FD_ISSET(mCtrlPipe[0], &read_fds)) - break; + if (FD_ISSET(mCtrlPipe[0], &read_fds)) { + char c = CtrlPipe_Shutdown; + TEMP_FAILURE_RETRY(read(mCtrlPipe[0], &c, 1)); + if (c == CtrlPipe_Shutdown) { + break; + } + continue; + } if (mListen && FD_ISSET(mSock, &read_fds)) { struct sockaddr addr; socklen_t alen; @@ -205,53 +214,111 @@ void SocketListener::runListener() { } /* Add all active clients to the pending list first */ - pendingList->clear(); + pendingList.clear(); pthread_mutex_lock(&mClientsLock); for (it = mClients->begin(); it != mClients->end(); ++it) { - int fd = (*it)->getSocket(); + SocketClient* c = *it; + // NB: calling out to an other object with mClientsLock held (safe) + int fd = c->getSocket(); if (FD_ISSET(fd, &read_fds)) { - pendingList->push_back(*it); + pendingList.push_back(c); + c->incRef(); } } pthread_mutex_unlock(&mClientsLock); /* Process the pending list, since it is owned by the thread, * there is no need to lock it */ - while (!pendingList->empty()) { + while (!pendingList.empty()) { /* Pop the first item from the list */ - it = pendingList->begin(); + it = pendingList.begin(); SocketClient* c = *it; - pendingList->erase(it); - /* Process it, if false is returned and our sockets are - * connection-based, remove and destroy it */ - if (!onDataAvailable(c) && mListen) { - /* Remove the client from our array */ - SLOGV("going to zap %d for %s", c->getSocket(), mSocketName); - pthread_mutex_lock(&mClientsLock); - for (it = mClients->begin(); it != mClients->end(); ++it) { - if (*it == c) { - mClients->erase(it); - break; - } - } - pthread_mutex_unlock(&mClientsLock); - /* Remove our reference to the client */ - c->decRef(); + pendingList.erase(it); + /* Process it, if false is returned, remove from list */ + if (!onDataAvailable(c)) { + release(c, false); } + c->decRef(); } } - delete pendingList; +} + +bool SocketListener::release(SocketClient* c, bool wakeup) { + bool ret = false; + /* if our sockets are connection-based, remove and destroy it */ + if (mListen && c) { + /* Remove the client from our array */ + SLOGV("going to zap %d for %s", c->getSocket(), mSocketName); + pthread_mutex_lock(&mClientsLock); + SocketClientCollection::iterator it; + for (it = mClients->begin(); it != mClients->end(); ++it) { + if (*it == c) { + mClients->erase(it); + ret = true; + break; + } + } + pthread_mutex_unlock(&mClientsLock); + if (ret) { + ret = c->decRef(); + if (wakeup) { + char b = CtrlPipe_Wakeup; + TEMP_FAILURE_RETRY(write(mCtrlPipe[1], &b, 1)); + } + } + } + return ret; } void SocketListener::sendBroadcast(int code, const char *msg, bool addErrno) { + SocketClientCollection safeList; + + /* Add all active clients to the safe list first */ + safeList.clear(); pthread_mutex_lock(&mClientsLock); SocketClientCollection::iterator i; for (i = mClients->begin(); i != mClients->end(); ++i) { + SocketClient* c = *i; + c->incRef(); + safeList.push_back(c); + } + pthread_mutex_unlock(&mClientsLock); + + while (!safeList.empty()) { + /* Pop the first item from the list */ + i = safeList.begin(); + SocketClient* c = *i; + safeList.erase(i); // broadcasts are unsolicited and should not include a cmd number - if ((*i)->sendMsg(code, msg, addErrno, false)) { + if (c->sendMsg(code, msg, addErrno, false)) { SLOGW("Error sending broadcast (%s)", strerror(errno)); } + c->decRef(); + } +} + +void SocketListener::runOnEachSocket(SocketClientCommand *command) { + SocketClientCollection safeList; + + /* Add all active clients to the safe list first */ + safeList.clear(); + pthread_mutex_lock(&mClientsLock); + SocketClientCollection::iterator i; + + for (i = mClients->begin(); i != mClients->end(); ++i) { + SocketClient* c = *i; + c->incRef(); + safeList.push_back(c); } pthread_mutex_unlock(&mClientsLock); + + while (!safeList.empty()) { + /* Pop the first item from the list */ + i = safeList.begin(); + SocketClient* c = *i; + safeList.erase(i); + command->runSocketCommand(c); + c->decRef(); + } } diff --git a/libutils/Android.mk b/libutils/Android.mk index 3024d2f..f7a4070 100644 --- a/libutils/Android.mk +++ b/libutils/Android.mk @@ -118,10 +118,12 @@ LOCAL_STATIC_LIBRARIES := \ libcutils LOCAL_SHARED_LIBRARIES := \ - libcorkscrew \ + libbacktrace \ liblog \ libdl +include external/stlport/libstlport.mk + LOCAL_MODULE:= libutils LOCAL_C_INCLUDES += external/safe-iop/include include $(BUILD_STATIC_LIBRARY) @@ -132,12 +134,15 @@ include $(CLEAR_VARS) LOCAL_MODULE:= libutils LOCAL_WHOLE_STATIC_LIBRARIES := libutils LOCAL_SHARED_LIBRARIES := \ - liblog \ + libbacktrace \ libcutils \ libdl \ - libcorkscrew + liblog \ + LOCAL_C_INCLUDES += external/safe-iop/include +include external/stlport/libstlport.mk + include $(BUILD_SHARED_LIBRARY) # Include subdirectory makefiles diff --git a/libutils/CallStack.cpp b/libutils/CallStack.cpp index 4ceaa7c..0bfb520 100644 --- a/libutils/CallStack.cpp +++ b/libutils/CallStack.cpp @@ -20,93 +20,33 @@ #include <utils/Printer.h> #include <utils/Errors.h> #include <utils/Log.h> -#include <corkscrew/backtrace.h> +#include <UniquePtr.h> + +#include <backtrace/Backtrace.h> namespace android { -CallStack::CallStack() : - mCount(0) { +CallStack::CallStack() { } -CallStack::CallStack(const char* logtag, int32_t ignoreDepth, int32_t maxDepth) { - this->update(ignoreDepth+1, maxDepth, CURRENT_THREAD); +CallStack::CallStack(const char* logtag, int32_t ignoreDepth) { + this->update(ignoreDepth+1); this->log(logtag); } -CallStack::CallStack(const CallStack& rhs) : - mCount(rhs.mCount) { - if (mCount) { - memcpy(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t)); - } -} - CallStack::~CallStack() { } -CallStack& CallStack::operator = (const CallStack& rhs) { - mCount = rhs.mCount; - if (mCount) { - memcpy(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t)); - } - return *this; -} - -bool CallStack::operator == (const CallStack& rhs) const { - if (mCount != rhs.mCount) - return false; - return !mCount || memcmp(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t)) == 0; -} - -bool CallStack::operator != (const CallStack& rhs) const { - return !operator == (rhs); -} - -bool CallStack::operator < (const CallStack& rhs) const { - if (mCount != rhs.mCount) - return mCount < rhs.mCount; - return memcmp(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t)) < 0; -} +void CallStack::update(int32_t ignoreDepth, pid_t tid) { + mFrameLines.clear(); -bool CallStack::operator >= (const CallStack& rhs) const { - return !operator < (rhs); -} - -bool CallStack::operator > (const CallStack& rhs) const { - if (mCount != rhs.mCount) - return mCount > rhs.mCount; - return memcmp(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t)) > 0; -} - -bool CallStack::operator <= (const CallStack& rhs) const { - return !operator > (rhs); -} - -const void* CallStack::operator [] (int index) const { - if (index >= int(mCount)) - return 0; - return reinterpret_cast<const void*>(mStack[index].absolute_pc); -} - -void CallStack::clear() { - mCount = 0; -} - -void CallStack::update(int32_t ignoreDepth, int32_t maxDepth, pid_t tid) { - if (maxDepth > MAX_DEPTH) { - maxDepth = MAX_DEPTH; + UniquePtr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid)); + if (!backtrace->Unwind(ignoreDepth)) { + ALOGW("%s: Failed to unwind callstack.", __FUNCTION__); } - ssize_t count; - - if (tid >= 0) { - count = unwind_backtrace_thread(tid, mStack, ignoreDepth + 1, maxDepth); - } else if (tid == CURRENT_THREAD) { - count = unwind_backtrace(mStack, ignoreDepth + 1, maxDepth); - } else { - ALOGE("%s: Invalid tid specified (%d)", __FUNCTION__, tid); - count = 0; + for (size_t i = 0; i < backtrace->NumFrames(); i++) { + mFrameLines.push_back(String8(backtrace->FormatFrameData(i).c_str())); } - - mCount = count > 0 ? count : 0; } void CallStack::log(const char* logtag, android_LogPriority priority, const char* prefix) const { @@ -129,16 +69,9 @@ String8 CallStack::toString(const char* prefix) const { } void CallStack::print(Printer& printer) const { - backtrace_symbol_t symbols[mCount]; - - get_backtrace_symbols(mStack, mCount, symbols); - for (size_t i = 0; i < mCount; i++) { - char line[MAX_BACKTRACE_LINE_LENGTH]; - format_backtrace_line(i, &mStack[i], &symbols[i], - line, MAX_BACKTRACE_LINE_LENGTH); - printer.printLine(line); + for (size_t i = 0; i < mFrameLines.size(); i++) { + printer.printLine(mFrameLines[i]); } - free_backtrace_symbols(symbols, mCount); } }; // namespace android diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp index c51df2d..9a2dd6c 100644 --- a/libutils/Looper.cpp +++ b/libutils/Looper.cpp @@ -43,7 +43,7 @@ void WeakMessageHandler::handleMessage(const Message& message) { // --- SimpleLooperCallback --- -SimpleLooperCallback::SimpleLooperCallback(ALooper_callbackFunc callback) : +SimpleLooperCallback::SimpleLooperCallback(Looper_callbackFunc callback) : mCallback(callback) { } @@ -139,7 +139,7 @@ sp<Looper> Looper::getForThread() { } sp<Looper> Looper::prepare(int opts) { - bool allowNonCallbacks = opts & ALOOPER_PREPARE_ALLOW_NON_CALLBACKS; + bool allowNonCallbacks = opts & PREPARE_ALLOW_NON_CALLBACKS; sp<Looper> looper = Looper::getForThread(); if (looper == NULL) { looper = new Looper(allowNonCallbacks); @@ -147,7 +147,7 @@ sp<Looper> Looper::prepare(int opts) { } if (looper->getAllowNonCallbacks() != allowNonCallbacks) { ALOGW("Looper already prepared for this thread with a different value for the " - "ALOOPER_PREPARE_ALLOW_NON_CALLBACKS option."); + "LOOPER_PREPARE_ALLOW_NON_CALLBACKS option."); } return looper; } @@ -212,7 +212,7 @@ int Looper::pollInner(int timeoutMillis) { } // Poll. - int result = ALOOPER_POLL_WAKE; + int result = POLL_WAKE; mResponses.clear(); mResponseIndex = 0; @@ -234,7 +234,7 @@ int Looper::pollInner(int timeoutMillis) { goto Done; } ALOGW("Poll failed with an unexpected error, errno=%d", errno); - result = ALOOPER_POLL_ERROR; + result = POLL_ERROR; goto Done; } @@ -243,7 +243,7 @@ int Looper::pollInner(int timeoutMillis) { #if DEBUG_POLL_AND_WAKE ALOGD("%p ~ pollOnce - timeout", this); #endif - result = ALOOPER_POLL_TIMEOUT; + result = POLL_TIMEOUT; goto Done; } @@ -265,10 +265,10 @@ int Looper::pollInner(int timeoutMillis) { ssize_t requestIndex = mRequests.indexOfKey(fd); if (requestIndex >= 0) { int events = 0; - if (epollEvents & EPOLLIN) events |= ALOOPER_EVENT_INPUT; - if (epollEvents & EPOLLOUT) events |= ALOOPER_EVENT_OUTPUT; - if (epollEvents & EPOLLERR) events |= ALOOPER_EVENT_ERROR; - if (epollEvents & EPOLLHUP) events |= ALOOPER_EVENT_HANGUP; + if (epollEvents & EPOLLIN) events |= EVENT_INPUT; + if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT; + if (epollEvents & EPOLLERR) events |= EVENT_ERROR; + if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP; pushResponse(events, mRequests.valueAt(requestIndex)); } else { ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is " @@ -304,7 +304,7 @@ Done: ; mLock.lock(); mSendingMessage = false; - result = ALOOPER_POLL_CALLBACK; + result = POLL_CALLBACK; } else { // The last message left at the head of the queue determines the next wakeup time. mNextMessageUptime = messageEnvelope.uptime; @@ -318,7 +318,7 @@ Done: ; // Invoke all response callbacks. for (size_t i = 0; i < mResponses.size(); i++) { Response& response = mResponses.editItemAt(i); - if (response.request.ident == ALOOPER_POLL_CALLBACK) { + if (response.request.ident == POLL_CALLBACK) { int fd = response.request.fd; int events = response.events; void* data = response.request.data; @@ -333,7 +333,7 @@ Done: ; // Clear the callback reference in the response structure promptly because we // will not clear the response vector itself until the next poll. response.request.callback.clear(); - result = ALOOPER_POLL_CALLBACK; + result = POLL_CALLBACK; } } return result; @@ -344,7 +344,7 @@ int Looper::pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outDat int result; do { result = pollOnce(timeoutMillis, outFd, outEvents, outData); - } while (result == ALOOPER_POLL_CALLBACK); + } while (result == POLL_CALLBACK); return result; } else { nsecs_t endTime = systemTime(SYSTEM_TIME_MONOTONIC) @@ -352,14 +352,14 @@ int Looper::pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outDat for (;;) { int result = pollOnce(timeoutMillis, outFd, outEvents, outData); - if (result != ALOOPER_POLL_CALLBACK) { + if (result != POLL_CALLBACK) { return result; } nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); timeoutMillis = toMillisecondTimeoutDelay(now, endTime); if (timeoutMillis == 0) { - return ALOOPER_POLL_TIMEOUT; + return POLL_TIMEOUT; } } } @@ -401,7 +401,7 @@ void Looper::pushResponse(int events, const Request& request) { mResponses.push(response); } -int Looper::addFd(int fd, int ident, int events, ALooper_callbackFunc callback, void* data) { +int Looper::addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data) { return addFd(fd, ident, events, callback ? new SimpleLooperCallback(callback) : NULL, data); } @@ -422,12 +422,12 @@ int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callb return -1; } } else { - ident = ALOOPER_POLL_CALLBACK; + ident = POLL_CALLBACK; } int epollEvents = 0; - if (events & ALOOPER_EVENT_INPUT) epollEvents |= EPOLLIN; - if (events & ALOOPER_EVENT_OUTPUT) epollEvents |= EPOLLOUT; + if (events & EVENT_INPUT) epollEvents |= EPOLLIN; + if (events & EVENT_OUTPUT) epollEvents |= EPOLLOUT; { // acquire lock AutoMutex _l(mLock); diff --git a/libutils/Printer.cpp b/libutils/Printer.cpp index ac729e0..263e740 100644 --- a/libutils/Printer.cpp +++ b/libutils/Printer.cpp @@ -145,6 +145,7 @@ void String8Printer::printLine(const char* string) { return; } + mTarget->append(mPrefix); mTarget->append(string); mTarget->append("\n"); } diff --git a/libutils/ProcessCallStack.cpp b/libutils/ProcessCallStack.cpp index f9340c5..f837bcb 100644 --- a/libutils/ProcessCallStack.cpp +++ b/libutils/ProcessCallStack.cpp @@ -123,7 +123,7 @@ void ProcessCallStack::clear() { mTimeUpdated = tm(); } -void ProcessCallStack::update(int32_t maxDepth) { +void ProcessCallStack::update() { DIR *dp; struct dirent *ep; struct dirent entry; @@ -181,14 +181,13 @@ void ProcessCallStack::update(int32_t maxDepth) { int ignoreDepth = (selfPid == tid) ? IGNORE_DEPTH_CURRENT_THREAD : 0; // Update thread's call stacks - CallStack& cs = threadInfo.callStack; - cs.update(ignoreDepth, maxDepth, tid); + threadInfo.callStack.update(ignoreDepth, tid); // Read/save thread name threadInfo.threadName = getThreadName(tid); ALOGV("%s: Got call stack for tid %d (size %zu)", - __FUNCTION__, tid, cs.size()); + __FUNCTION__, tid, threadInfo.callStack.size()); } if (code != 0) { // returns positive error value on error ALOGE("%s: Failed to readdir from %s (errno = %d, '%s')", @@ -221,13 +220,12 @@ void ProcessCallStack::printInternal(Printer& printer, Printer& csPrinter) const for (size_t i = 0; i < mThreadMap.size(); ++i) { pid_t tid = mThreadMap.keyAt(i); const ThreadInfo& threadInfo = mThreadMap.valueAt(i); - const CallStack& cs = threadInfo.callStack; const String8& threadName = threadInfo.threadName; printer.printLine(""); printer.printFormatLine("\"%s\" sysTid=%d", threadName.string(), tid); - cs.print(csPrinter); + threadInfo.callStack.print(csPrinter); } dumpProcessFooter(printer, getpid()); diff --git a/libutils/SystemClock.cpp b/libutils/SystemClock.cpp index ac8da88..413250f 100644 --- a/libutils/SystemClock.cpp +++ b/libutils/SystemClock.cpp @@ -119,22 +119,6 @@ int64_t elapsedRealtimeNano() static volatile int prevMethod; #endif -#if 0 - /* - * b/7100774 - * clock_gettime appears to have clock skews and can sometimes return - * backwards values. Disable its use until we find out what's wrong. - */ - result = clock_gettime(CLOCK_BOOTTIME, &ts); - if (result == 0) { - timestamp = seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec; - checkTimeStamps(timestamp, &prevTimestamp, &prevMethod, - METHOD_CLOCK_GETTIME); - return timestamp; - } -#endif - - // CLOCK_BOOTTIME doesn't exist, fallback to /dev/alarm static int s_fd = -1; if (s_fd == -1) { @@ -153,6 +137,15 @@ int64_t elapsedRealtimeNano() return timestamp; } + // /dev/alarm doesn't exist, fallback to CLOCK_BOOTTIME + result = clock_gettime(CLOCK_BOOTTIME, &ts); + if (result == 0) { + timestamp = seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec; + checkTimeStamps(timestamp, &prevTimestamp, &prevMethod, + METHOD_CLOCK_GETTIME); + return timestamp; + } + // XXX: there was an error, probably because the driver didn't // exist ... this should return // a real error, like an exception! diff --git a/libutils/VectorImpl.cpp b/libutils/VectorImpl.cpp index 3bcf54b..de65a6c 100644 --- a/libutils/VectorImpl.cpp +++ b/libutils/VectorImpl.cpp @@ -411,7 +411,11 @@ void* VectorImpl::_grow(size_t where, size_t amount) { const SharedBuffer* cur_sb = SharedBuffer::bufferFromData(mStorage); SharedBuffer* sb = cur_sb->editResize(new_alloc_size); - mStorage = sb->data(); + if (sb) { + mStorage = sb->data(); + } else { + return NULL; + } } else { SharedBuffer* sb = SharedBuffer::alloc(new_alloc_size); if (sb) { @@ -426,6 +430,8 @@ void* VectorImpl::_grow(size_t where, size_t amount) } release_storage(); mStorage = const_cast<void*>(array); + } else { + return NULL; } } } else { @@ -472,7 +478,11 @@ void VectorImpl::_shrink(size_t where, size_t amount) { const SharedBuffer* cur_sb = SharedBuffer::bufferFromData(mStorage); SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize); - mStorage = sb->data(); + if (sb) { + mStorage = sb->data(); + } else { + return; + } } else { SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); if (sb) { @@ -487,6 +497,8 @@ void VectorImpl::_shrink(size_t where, size_t amount) } release_storage(); mStorage = const_cast<void*>(array); + } else{ + return; } } } else { diff --git a/libutils/tests/BitSet_test.cpp b/libutils/tests/BitSet_test.cpp index 752e56d..38b668a 100644 --- a/libutils/tests/BitSet_test.cpp +++ b/libutils/tests/BitSet_test.cpp @@ -23,7 +23,7 @@ namespace android { -class BitSetTest : public testing::Test { +class BitSet32Test : public testing::Test { protected: BitSet32 b1; BitSet32 b2; @@ -34,7 +34,7 @@ protected: }; -TEST_F(BitSetTest, BitWiseOr) { +TEST_F(BitSet32Test, BitWiseOr) { b1.markBit(2); b2.markBit(4); @@ -49,7 +49,7 @@ TEST_F(BitSetTest, BitWiseOr) { EXPECT_TRUE(b1.hasBit(2) && b1.hasBit(4)); EXPECT_TRUE(b2.hasBit(4) && b2.count() == 1u); } -TEST_F(BitSetTest, BitWiseAnd_Disjoint) { +TEST_F(BitSet32Test, BitWiseAnd_Disjoint) { b1.markBit(2); b1.markBit(4); b1.markBit(6); @@ -65,7 +65,7 @@ TEST_F(BitSetTest, BitWiseAnd_Disjoint) { EXPECT_TRUE(b1.hasBit(2) && b1.hasBit(4) && b1.hasBit(6)); } -TEST_F(BitSetTest, BitWiseAnd_NonDisjoint) { +TEST_F(BitSet32Test, BitWiseAnd_NonDisjoint) { b1.markBit(2); b1.markBit(4); b1.markBit(6); @@ -84,4 +84,187 @@ TEST_F(BitSetTest, BitWiseAnd_NonDisjoint) { EXPECT_EQ(b2.count(), 3u); EXPECT_TRUE(b2.hasBit(3) && b2.hasBit(6) && b2.hasBit(9)); } + +TEST_F(BitSet32Test, MarkFirstUnmarkedBit) { + b1.markBit(1); + + b1.markFirstUnmarkedBit(); + EXPECT_EQ(b1.count(), 2u); + EXPECT_TRUE(b1.hasBit(0) && b1.hasBit(1)); + + b1.markFirstUnmarkedBit(); + EXPECT_EQ(b1.count(), 3u); + EXPECT_TRUE(b1.hasBit(0) && b1.hasBit(1) && b1.hasBit(2)); +} + +TEST_F(BitSet32Test, ClearFirstMarkedBit) { + b1.markBit(0); + b1.markBit(10); + + b1.clearFirstMarkedBit(); + EXPECT_EQ(b1.count(), 1u); + EXPECT_TRUE(b1.hasBit(10)); + + b1.markBit(30); + b1.clearFirstMarkedBit(); + EXPECT_EQ(b1.count(), 1u); + EXPECT_TRUE(b1.hasBit(30)); +} + +TEST_F(BitSet32Test, ClearLastMarkedBit) { + b1.markBit(10); + b1.markBit(31); + + b1.clearLastMarkedBit(); + EXPECT_EQ(b1.count(), 1u); + EXPECT_TRUE(b1.hasBit(10)); + + b1.markBit(5); + b1.clearLastMarkedBit(); + EXPECT_EQ(b1.count(), 1u); + EXPECT_TRUE(b1.hasBit(5)); +} + +TEST_F(BitSet32Test, FillAndClear) { + EXPECT_TRUE(b1.isEmpty()); + for (size_t i = 0; i < 32; i++) { + b1.markFirstUnmarkedBit(); + } + EXPECT_TRUE(b1.isFull()); + b1.clear(); + EXPECT_TRUE(b1.isEmpty()); +} + +TEST_F(BitSet32Test, GetIndexOfBit) { + b1.markBit(1); + b1.markBit(4); + EXPECT_EQ(b1.getIndexOfBit(1), 0); + EXPECT_EQ(b1.getIndexOfBit(4), 1); + b1.markFirstUnmarkedBit(); + EXPECT_EQ(b1.getIndexOfBit(1), 1); + EXPECT_EQ(b1.getIndexOfBit(4), 2); +} + +class BitSet64Test : public testing::Test { +protected: + BitSet64 b1; + BitSet64 b2; + virtual void TearDown() { + b1.clear(); + b2.clear(); + } +}; + + +TEST_F(BitSet64Test, BitWiseOr) { + b1.markBit(20); + b2.markBit(40); + + BitSet64 tmp = b1 | b2; + EXPECT_EQ(tmp.count(), 2u); + EXPECT_TRUE(tmp.hasBit(20) && tmp.hasBit(40)); + // Check that the operator is symmetric + EXPECT_TRUE((b2 | b1) == (b1 | b2)); + + b1 |= b2; + EXPECT_EQ(b1.count(), 2u); + EXPECT_TRUE(b1.hasBit(20) && b1.hasBit(40)); + EXPECT_TRUE(b2.hasBit(40) && b2.count() == 1u); +} +TEST_F(BitSet64Test, BitWiseAnd_Disjoint) { + b1.markBit(20); + b1.markBit(40); + b1.markBit(60); + + BitSet64 tmp = b1 & b2; + EXPECT_TRUE(tmp.isEmpty()); + // Check that the operator is symmetric + EXPECT_TRUE((b2 & b1) == (b1 & b2)); + + b2 &= b1; + EXPECT_TRUE(b2.isEmpty()); + EXPECT_EQ(b1.count(), 3u); + EXPECT_TRUE(b1.hasBit(20) && b1.hasBit(40) && b1.hasBit(60)); +} + +TEST_F(BitSet64Test, BitWiseAnd_NonDisjoint) { + b1.markBit(20); + b1.markBit(40); + b1.markBit(60); + b2.markBit(30); + b2.markBit(60); + b2.markBit(63); + + BitSet64 tmp = b1 & b2; + EXPECT_EQ(tmp.count(), 1u); + EXPECT_TRUE(tmp.hasBit(60)); + // Check that the operator is symmetric + EXPECT_TRUE((b2 & b1) == (b1 & b2)); + + b1 &= b2; + EXPECT_EQ(b1.count(), 1u); + EXPECT_EQ(b2.count(), 3u); + EXPECT_TRUE(b2.hasBit(30) && b2.hasBit(60) && b2.hasBit(63)); +} + +TEST_F(BitSet64Test, MarkFirstUnmarkedBit) { + b1.markBit(1); + + b1.markFirstUnmarkedBit(); + EXPECT_EQ(b1.count(), 2u); + EXPECT_TRUE(b1.hasBit(0) && b1.hasBit(1)); + + b1.markFirstUnmarkedBit(); + EXPECT_EQ(b1.count(), 3u); + EXPECT_TRUE(b1.hasBit(0) && b1.hasBit(1) && b1.hasBit(2)); +} + +TEST_F(BitSet64Test, ClearFirstMarkedBit) { + b1.markBit(0); + b1.markBit(10); + + b1.clearFirstMarkedBit(); + EXPECT_EQ(b1.count(), 1u); + EXPECT_TRUE(b1.hasBit(10)); + + b1.markBit(50); + b1.clearFirstMarkedBit(); + EXPECT_EQ(b1.count(), 1u); + EXPECT_TRUE(b1.hasBit(50)); +} + +TEST_F(BitSet64Test, ClearLastMarkedBit) { + b1.markBit(10); + b1.markBit(63); + + b1.clearLastMarkedBit(); + EXPECT_EQ(b1.count(), 1u); + EXPECT_TRUE(b1.hasBit(10)); + + b1.markBit(5); + b1.clearLastMarkedBit(); + EXPECT_EQ(b1.count(), 1u); + EXPECT_TRUE(b1.hasBit(5)); +} + +TEST_F(BitSet64Test, FillAndClear) { + EXPECT_TRUE(b1.isEmpty()); + for (size_t i = 0; i < 64; i++) { + b1.markFirstUnmarkedBit(); + } + EXPECT_TRUE(b1.isFull()); + b1.clear(); + EXPECT_TRUE(b1.isEmpty()); +} + +TEST_F(BitSet64Test, GetIndexOfBit) { + b1.markBit(10); + b1.markBit(40); + EXPECT_EQ(b1.getIndexOfBit(10), 0); + EXPECT_EQ(b1.getIndexOfBit(40), 1); + b1.markFirstUnmarkedBit(); + EXPECT_EQ(b1.getIndexOfBit(10), 1); + EXPECT_EQ(b1.getIndexOfBit(40), 2); +} + } // namespace android diff --git a/libutils/tests/Looper_test.cpp b/libutils/tests/Looper_test.cpp index 8bf2ba2..00077e6 100644 --- a/libutils/tests/Looper_test.cpp +++ b/libutils/tests/Looper_test.cpp @@ -119,8 +119,8 @@ TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndNotAwoken_WaitsForTimeout) { EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal timeout"; - EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) - << "pollOnce result should be ALOOPER_POLL_TIMEOUT"; + EXPECT_EQ(Looper::POLL_TIMEOUT, result) + << "pollOnce result should be LOOPER_POLL_TIMEOUT"; } TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndAwokenBeforeWaiting_ImmediatelyReturns) { @@ -132,8 +132,8 @@ TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndAwokenBeforeWaiting_Immediately EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. zero because wake() was called before waiting"; - EXPECT_EQ(ALOOPER_POLL_WAKE, result) - << "pollOnce result should be ALOOPER_POLL_CALLBACK because loop was awoken"; + EXPECT_EQ(Looper::POLL_WAKE, result) + << "pollOnce result should be Looper::POLL_CALLBACK because loop was awoken"; } TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndAwokenWhileWaiting_PromptlyReturns) { @@ -146,8 +146,8 @@ TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndAwokenWhileWaiting_PromptlyRetu EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal wake delay"; - EXPECT_EQ(ALOOPER_POLL_WAKE, result) - << "pollOnce result should be ALOOPER_POLL_CALLBACK because loop was awoken"; + EXPECT_EQ(Looper::POLL_WAKE, result) + << "pollOnce result should be Looper::POLL_CALLBACK because loop was awoken"; } TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndNoRegisteredFDs_ImmediatelyReturns) { @@ -157,15 +157,15 @@ TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndNoRegisteredFDs_ImmediatelyReturns EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should be approx. zero"; - EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) - << "pollOnce result should be ALOOPER_POLL_TIMEOUT"; + EXPECT_EQ(Looper::POLL_TIMEOUT, result) + << "pollOnce result should be Looper::POLL_TIMEOUT"; } TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndNoSignalledFDs_ImmediatelyReturns) { Pipe pipe; StubCallbackHandler handler(true); - handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT); StopWatch stopWatch("pollOnce"); int result = mLooper->pollOnce(0); @@ -173,8 +173,8 @@ TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndNoSignalledFDs_ImmediatelyReturns) EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should be approx. zero"; - EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) - << "pollOnce result should be ALOOPER_POLL_TIMEOUT"; + EXPECT_EQ(Looper::POLL_TIMEOUT, result) + << "pollOnce result should be Looper::POLL_TIMEOUT"; EXPECT_EQ(0, handler.callbackCount) << "callback should not have been invoked because FD was not signalled"; } @@ -184,7 +184,7 @@ TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndSignalledFD_ImmediatelyInvokesCall StubCallbackHandler handler(true); ASSERT_EQ(OK, pipe.writeSignal()); - handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT); StopWatch stopWatch("pollOnce"); int result = mLooper->pollOnce(0); @@ -192,21 +192,21 @@ TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndSignalledFD_ImmediatelyInvokesCall EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should be approx. zero"; - EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) - << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled"; + EXPECT_EQ(Looper::POLL_CALLBACK, result) + << "pollOnce result should be Looper::POLL_CALLBACK because FD was signalled"; EXPECT_EQ(1, handler.callbackCount) << "callback should be invoked exactly once"; EXPECT_EQ(pipe.receiveFd, handler.fd) << "callback should have received pipe fd as parameter"; - EXPECT_EQ(ALOOPER_EVENT_INPUT, handler.events) - << "callback should have received ALOOPER_EVENT_INPUT as events"; + EXPECT_EQ(Looper::EVENT_INPUT, handler.events) + << "callback should have received Looper::EVENT_INPUT as events"; } TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndNoSignalledFDs_WaitsForTimeoutAndReturns) { Pipe pipe; StubCallbackHandler handler(true); - handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT); StopWatch stopWatch("pollOnce"); int result = mLooper->pollOnce(100); @@ -214,8 +214,8 @@ TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndNoSignalledFDs_WaitsForTimeoutA EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal timeout"; - EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) - << "pollOnce result should be ALOOPER_POLL_TIMEOUT"; + EXPECT_EQ(Looper::POLL_TIMEOUT, result) + << "pollOnce result should be Looper::POLL_TIMEOUT"; EXPECT_EQ(0, handler.callbackCount) << "callback should not have been invoked because FD was not signalled"; } @@ -225,7 +225,7 @@ TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDBeforeWaiting_Immedi StubCallbackHandler handler(true); pipe.writeSignal(); - handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT); StopWatch stopWatch("pollOnce"); int result = mLooper->pollOnce(100); @@ -235,14 +235,14 @@ TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDBeforeWaiting_Immedi << "signal should actually have been written"; EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should be approx. zero"; - EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) - << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled"; + EXPECT_EQ(Looper::POLL_CALLBACK, result) + << "pollOnce result should be Looper::POLL_CALLBACK because FD was signalled"; EXPECT_EQ(1, handler.callbackCount) << "callback should be invoked exactly once"; EXPECT_EQ(pipe.receiveFd, handler.fd) << "callback should have received pipe fd as parameter"; - EXPECT_EQ(ALOOPER_EVENT_INPUT, handler.events) - << "callback should have received ALOOPER_EVENT_INPUT as events"; + EXPECT_EQ(Looper::EVENT_INPUT, handler.events) + << "callback should have received Looper::EVENT_INPUT as events"; } TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDWhileWaiting_PromptlyInvokesCallbackAndReturns) { @@ -250,7 +250,7 @@ TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDWhileWaiting_Promptl StubCallbackHandler handler(true); sp<DelayedWriteSignal> delayedWriteSignal = new DelayedWriteSignal(100, & pipe); - handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT); delayedWriteSignal->run(); StopWatch stopWatch("pollOnce"); @@ -261,21 +261,21 @@ TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDWhileWaiting_Promptl << "signal should actually have been written"; EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal signal delay"; - EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) - << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled"; + EXPECT_EQ(Looper::POLL_CALLBACK, result) + << "pollOnce result should be Looper::POLL_CALLBACK because FD was signalled"; EXPECT_EQ(1, handler.callbackCount) << "callback should be invoked exactly once"; EXPECT_EQ(pipe.receiveFd, handler.fd) << "callback should have received pipe fd as parameter"; - EXPECT_EQ(ALOOPER_EVENT_INPUT, handler.events) - << "callback should have received ALOOPER_EVENT_INPUT as events"; + EXPECT_EQ(Looper::EVENT_INPUT, handler.events) + << "callback should have received Looper::EVENT_INPUT as events"; } TEST_F(LooperTest, PollOnce_WhenCallbackAddedThenRemoved_CallbackShouldNotBeInvoked) { Pipe pipe; StubCallbackHandler handler(true); - handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT); pipe.writeSignal(); // would cause FD to be considered signalled mLooper->removeFd(pipe.receiveFd); @@ -287,8 +287,8 @@ TEST_F(LooperTest, PollOnce_WhenCallbackAddedThenRemoved_CallbackShouldNotBeInvo << "signal should actually have been written"; EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal timeout because FD was no longer registered"; - EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) - << "pollOnce result should be ALOOPER_POLL_TIMEOUT"; + EXPECT_EQ(Looper::POLL_TIMEOUT, result) + << "pollOnce result should be Looper::POLL_TIMEOUT"; EXPECT_EQ(0, handler.callbackCount) << "callback should not be invoked"; } @@ -297,7 +297,7 @@ TEST_F(LooperTest, PollOnce_WhenCallbackReturnsFalse_CallbackShouldNotBeInvokedA Pipe pipe; StubCallbackHandler handler(false); - handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT); // First loop: Callback is registered and FD is signalled. pipe.writeSignal(); @@ -310,8 +310,8 @@ TEST_F(LooperTest, PollOnce_WhenCallbackReturnsFalse_CallbackShouldNotBeInvokedA << "signal should actually have been written"; EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal zero because FD was already signalled"; - EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) - << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled"; + EXPECT_EQ(Looper::POLL_CALLBACK, result) + << "pollOnce result should be Looper::POLL_CALLBACK because FD was signalled"; EXPECT_EQ(1, handler.callbackCount) << "callback should be invoked"; @@ -326,8 +326,8 @@ TEST_F(LooperTest, PollOnce_WhenCallbackReturnsFalse_CallbackShouldNotBeInvokedA << "signal should actually have been written"; EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal zero because timeout was zero"; - EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) - << "pollOnce result should be ALOOPER_POLL_TIMEOUT"; + EXPECT_EQ(Looper::POLL_TIMEOUT, result) + << "pollOnce result should be Looper::POLL_TIMEOUT"; EXPECT_EQ(1, handler.callbackCount) << "callback should not be invoked this time"; } @@ -339,7 +339,7 @@ TEST_F(LooperTest, PollOnce_WhenNonCallbackFdIsSignalled_ReturnsIdent) { Pipe pipe; pipe.writeSignal(); - mLooper->addFd(pipe.receiveFd, expectedIdent, ALOOPER_EVENT_INPUT, NULL, expectedData); + mLooper->addFd(pipe.receiveFd, expectedIdent, Looper::EVENT_INPUT, NULL, expectedData); StopWatch stopWatch("pollOnce"); int fd; @@ -356,15 +356,15 @@ TEST_F(LooperTest, PollOnce_WhenNonCallbackFdIsSignalled_ReturnsIdent) { << "pollOnce result should be the ident of the FD that was signalled"; EXPECT_EQ(pipe.receiveFd, fd) << "pollOnce should have returned the received pipe fd"; - EXPECT_EQ(ALOOPER_EVENT_INPUT, events) - << "pollOnce should have returned ALOOPER_EVENT_INPUT as events"; + EXPECT_EQ(Looper::EVENT_INPUT, events) + << "pollOnce should have returned Looper::EVENT_INPUT as events"; EXPECT_EQ(expectedData, data) << "pollOnce should have returned the data"; } TEST_F(LooperTest, AddFd_WhenCallbackAdded_ReturnsOne) { Pipe pipe; - int result = mLooper->addFd(pipe.receiveFd, 0, ALOOPER_EVENT_INPUT, NULL, NULL); + int result = mLooper->addFd(pipe.receiveFd, 0, Looper::EVENT_INPUT, NULL, NULL); EXPECT_EQ(1, result) << "addFd should return 1 because FD was added"; @@ -372,7 +372,7 @@ TEST_F(LooperTest, AddFd_WhenCallbackAdded_ReturnsOne) { TEST_F(LooperTest, AddFd_WhenIdentIsNegativeAndCallbackIsNull_ReturnsError) { Pipe pipe; - int result = mLooper->addFd(pipe.receiveFd, -1, ALOOPER_EVENT_INPUT, NULL, NULL); + int result = mLooper->addFd(pipe.receiveFd, -1, Looper::EVENT_INPUT, NULL, NULL); EXPECT_EQ(-1, result) << "addFd should return -1 because arguments were invalid"; @@ -397,7 +397,7 @@ TEST_F(LooperTest, RemoveFd_WhenCallbackNotAdded_ReturnsZero) { TEST_F(LooperTest, RemoveFd_WhenCallbackAddedThenRemovedTwice_ReturnsOnceFirstTimeAndReturnsZeroSecondTime) { Pipe pipe; StubCallbackHandler handler(false); - handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT); // First time. int result = mLooper->removeFd(pipe.receiveFd); @@ -417,8 +417,8 @@ TEST_F(LooperTest, PollOnce_WhenCallbackAddedTwice_OnlySecondCallbackShouldBeInv StubCallbackHandler handler1(true); StubCallbackHandler handler2(true); - handler1.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); - handler2.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); // replace it + handler1.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT); + handler2.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT); // replace it pipe.writeSignal(); // would cause FD to be considered signalled StopWatch stopWatch("pollOnce"); @@ -429,8 +429,8 @@ TEST_F(LooperTest, PollOnce_WhenCallbackAddedTwice_OnlySecondCallbackShouldBeInv << "signal should actually have been written"; EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. zero because FD was already signalled"; - EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) - << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled"; + EXPECT_EQ(Looper::POLL_CALLBACK, result) + << "pollOnce result should be Looper::POLL_CALLBACK because FD was signalled"; EXPECT_EQ(0, handler1.callbackCount) << "original handler callback should not be invoked because it was replaced"; EXPECT_EQ(1, handler2.callbackCount) @@ -447,8 +447,8 @@ TEST_F(LooperTest, SendMessage_WhenOneMessageIsEnqueue_ShouldInvokeHandlerDuring EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. zero because message was already sent"; - EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) - << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + EXPECT_EQ(Looper::POLL_CALLBACK, result) + << "pollOnce result should be Looper::POLL_CALLBACK because message was sent"; EXPECT_EQ(size_t(1), handler->messages.size()) << "handled message"; EXPECT_EQ(MSG_TEST1, handler->messages[0].what) @@ -469,8 +469,8 @@ TEST_F(LooperTest, SendMessage_WhenMultipleMessagesAreEnqueued_ShouldInvokeHandl EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. zero because message was already sent"; - EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) - << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + EXPECT_EQ(Looper::POLL_CALLBACK, result) + << "pollOnce result should be Looper::POLL_CALLBACK because message was sent"; EXPECT_EQ(size_t(3), handler1->messages.size()) << "handled message"; EXPECT_EQ(MSG_TEST1, handler1->messages[0].what) @@ -495,8 +495,8 @@ TEST_F(LooperTest, SendMessageDelayed_WhenSentToTheFuture_ShouldInvokeHandlerAft EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "first poll should end quickly because next message timeout was computed"; - EXPECT_EQ(ALOOPER_POLL_WAKE, result) - << "pollOnce result should be ALOOPER_POLL_WAKE due to wakeup"; + EXPECT_EQ(Looper::POLL_WAKE, result) + << "pollOnce result should be Looper::POLL_WAKE due to wakeup"; EXPECT_EQ(size_t(0), handler->messages.size()) << "no message handled yet"; @@ -509,16 +509,16 @@ TEST_F(LooperTest, SendMessageDelayed_WhenSentToTheFuture_ShouldInvokeHandlerAft << "handled message"; EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) << "second poll should end around the time of the delayed message dispatch"; - EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) - << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + EXPECT_EQ(Looper::POLL_CALLBACK, result) + << "pollOnce result should be Looper::POLL_CALLBACK because message was sent"; result = mLooper->pollOnce(100); elapsedMillis = ns2ms(stopWatch.elapsedTime()); EXPECT_NEAR(100 + 100, elapsedMillis, TIMING_TOLERANCE_MS) << "third poll should timeout"; - EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) - << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there were no messages left"; + EXPECT_EQ(Looper::POLL_TIMEOUT, result) + << "pollOnce result should be Looper::POLL_TIMEOUT because there were no messages left"; } TEST_F(LooperTest, SendMessageDelayed_WhenSentToThePast_ShouldInvokeHandlerDuringNextPoll) { @@ -531,8 +531,8 @@ TEST_F(LooperTest, SendMessageDelayed_WhenSentToThePast_ShouldInvokeHandlerDurin EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. zero because message was already sent"; - EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) - << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + EXPECT_EQ(Looper::POLL_CALLBACK, result) + << "pollOnce result should be Looper::POLL_CALLBACK because message was sent"; EXPECT_EQ(size_t(1), handler->messages.size()) << "handled message"; EXPECT_EQ(MSG_TEST1, handler->messages[0].what) @@ -549,8 +549,8 @@ TEST_F(LooperTest, SendMessageDelayed_WhenSentToThePresent_ShouldInvokeHandlerDu EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. zero because message was already sent"; - EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) - << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + EXPECT_EQ(Looper::POLL_CALLBACK, result) + << "pollOnce result should be Looper::POLL_CALLBACK because message was sent"; EXPECT_EQ(size_t(1), handler->messages.size()) << "handled message"; EXPECT_EQ(MSG_TEST1, handler->messages[0].what) @@ -568,8 +568,8 @@ TEST_F(LooperTest, SendMessageAtTime_WhenSentToTheFuture_ShouldInvokeHandlerAfte EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "first poll should end quickly because next message timeout was computed"; - EXPECT_EQ(ALOOPER_POLL_WAKE, result) - << "pollOnce result should be ALOOPER_POLL_WAKE due to wakeup"; + EXPECT_EQ(Looper::POLL_WAKE, result) + << "pollOnce result should be Looper::POLL_WAKE due to wakeup"; EXPECT_EQ(size_t(0), handler->messages.size()) << "no message handled yet"; @@ -582,16 +582,16 @@ TEST_F(LooperTest, SendMessageAtTime_WhenSentToTheFuture_ShouldInvokeHandlerAfte << "handled message"; EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) << "second poll should end around the time of the delayed message dispatch"; - EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) - << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + EXPECT_EQ(Looper::POLL_CALLBACK, result) + << "pollOnce result should be Looper::POLL_CALLBACK because message was sent"; result = mLooper->pollOnce(100); elapsedMillis = ns2ms(stopWatch.elapsedTime()); EXPECT_NEAR(100 + 100, elapsedMillis, TIMING_TOLERANCE_MS) << "third poll should timeout"; - EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) - << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there were no messages left"; + EXPECT_EQ(Looper::POLL_TIMEOUT, result) + << "pollOnce result should be Looper::POLL_TIMEOUT because there were no messages left"; } TEST_F(LooperTest, SendMessageAtTime_WhenSentToThePast_ShouldInvokeHandlerDuringNextPoll) { @@ -605,8 +605,8 @@ TEST_F(LooperTest, SendMessageAtTime_WhenSentToThePast_ShouldInvokeHandlerDuring EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. zero because message was already sent"; - EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) - << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + EXPECT_EQ(Looper::POLL_CALLBACK, result) + << "pollOnce result should be Looper::POLL_CALLBACK because message was sent"; EXPECT_EQ(size_t(1), handler->messages.size()) << "handled message"; EXPECT_EQ(MSG_TEST1, handler->messages[0].what) @@ -624,8 +624,8 @@ TEST_F(LooperTest, SendMessageAtTime_WhenSentToThePresent_ShouldInvokeHandlerDur EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. zero because message was already sent"; - EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) - << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + EXPECT_EQ(Looper::POLL_CALLBACK, result) + << "pollOnce result should be Looper::POLL_CALLBACK because message was sent"; EXPECT_EQ(size_t(1), handler->messages.size()) << "handled message"; EXPECT_EQ(MSG_TEST1, handler->messages[0].what) @@ -645,15 +645,15 @@ TEST_F(LooperTest, RemoveMessage_WhenRemovingAllMessagesForHandler_ShouldRemoveT EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. zero because message was sent so looper was awoken"; - EXPECT_EQ(ALOOPER_POLL_WAKE, result) - << "pollOnce result should be ALOOPER_POLL_WAKE because looper was awoken"; + EXPECT_EQ(Looper::POLL_WAKE, result) + << "pollOnce result should be Looper::POLL_WAKE because looper was awoken"; EXPECT_EQ(size_t(0), handler->messages.size()) << "no messages to handle"; result = mLooper->pollOnce(0); - EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) - << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there was nothing to do"; + EXPECT_EQ(Looper::POLL_TIMEOUT, result) + << "pollOnce result should be Looper::POLL_TIMEOUT because there was nothing to do"; EXPECT_EQ(size_t(0), handler->messages.size()) << "no messages to handle"; } @@ -673,8 +673,8 @@ TEST_F(LooperTest, RemoveMessage_WhenRemovingSomeMessagesForHandler_ShouldRemove EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. zero because message was sent so looper was awoken"; - EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) - << "pollOnce result should be ALOOPER_POLL_CALLBACK because two messages were sent"; + EXPECT_EQ(Looper::POLL_CALLBACK, result) + << "pollOnce result should be Looper::POLL_CALLBACK because two messages were sent"; EXPECT_EQ(size_t(2), handler->messages.size()) << "no messages to handle"; EXPECT_EQ(MSG_TEST2, handler->messages[0].what) @@ -684,8 +684,8 @@ TEST_F(LooperTest, RemoveMessage_WhenRemovingSomeMessagesForHandler_ShouldRemove result = mLooper->pollOnce(0); - EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) - << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there was nothing to do"; + EXPECT_EQ(Looper::POLL_TIMEOUT, result) + << "pollOnce result should be Looper::POLL_TIMEOUT because there was nothing to do"; EXPECT_EQ(size_t(2), handler->messages.size()) << "no more messages to handle"; } diff --git a/libziparchive/Android.mk b/libziparchive/Android.mk new file mode 100644 index 0000000..e754c3b --- /dev/null +++ b/libziparchive/Android.mk @@ -0,0 +1,69 @@ +# +# 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. + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +source_files := \ + zip_archive.h \ + zip_archive.cc + +includes := external/zlib + +LOCAL_CPP_EXTENSION := .cc +LOCAL_SRC_FILES := ${source_files} + +LOCAL_STATIC_LIBRARIES := libz +LOCAL_SHARED_LIBRARIES := libutils +LOCAL_MODULE:= libziparchive + +LOCAL_C_INCLUDES += ${includes} +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := libziparchive +LOCAL_CPP_EXTENSION := .cc +LOCAL_SRC_FILES := ${source_files} +LOCAL_C_INCLUDES += ${includes} + +LOCAL_STATIC_LIBRARIES := libz libutils +LOCAL_MODULE:= libziparchive-host +include $(BUILD_HOST_STATIC_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := ziparchive-tests +LOCAL_CPP_EXTENSION := .cc +LOCAL_CFLAGS += \ + -DGTEST_OS_LINUX_ANDROID \ + -DGTEST_HAS_STD_STRING +LOCAL_SRC_FILES := zip_archive_test.cc +LOCAL_SHARED_LIBRARIES := liblog +LOCAL_STATIC_LIBRARIES := libziparchive libz libgtest libgtest_main libutils +include $(BUILD_NATIVE_TEST) + +include $(CLEAR_VARS) +LOCAL_MODULE := ziparchive-tests-host +LOCAL_CPP_EXTENSION := .cc +LOCAL_CFLAGS += \ + -DGTEST_OS_LINUX \ + -DGTEST_HAS_STD_STRING +LOCAL_SRC_FILES := zip_archive_test.cc +LOCAL_STATIC_LIBRARIES := libziparchive-host \ + libz \ + libgtest_host \ + libgtest_main_host \ + liblog \ + libutils +include $(BUILD_HOST_NATIVE_TEST) diff --git a/libziparchive/testdata/valid.zip b/libziparchive/testdata/valid.zip Binary files differnew file mode 100644 index 0000000..9e7cb78 --- /dev/null +++ b/libziparchive/testdata/valid.zip diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc new file mode 100644 index 0000000..a23d4ae --- /dev/null +++ b/libziparchive/zip_archive.cc @@ -0,0 +1,1055 @@ +/* + * Copyright (C) 2008 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. + */ + +/* + * Read-only access to Zip archives, with minimal heap allocation. + */ +#include "ziparchive/zip_archive.h" + +#include <zlib.h> + +#include <assert.h> +#include <errno.h> +#include <limits.h> +#include <log/log.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <utils/FileMap.h> + +#include <JNIHelp.h> // TEMP_FAILURE_RETRY may or may not be in unistd + +// This is for windows. If we don't open a file in binary mode, weirds +// things will happen. +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +/* + * Zip file constants. + */ +static const uint32_t kEOCDSignature = 0x06054b50; +static const uint32_t kEOCDLen = 2; +static const uint32_t kEOCDNumEntries = 8; // offset to #of entries in file +static const uint32_t kEOCDSize = 12; // size of the central directory +static const uint32_t kEOCDFileOffset = 16; // offset to central directory + +static const uint32_t kMaxCommentLen = 65535; // longest possible in ushort +static const uint32_t kMaxEOCDSearch = (kMaxCommentLen + kEOCDLen); + +static const uint32_t kLFHSignature = 0x04034b50; +static const uint32_t kLFHLen = 30; // excluding variable-len fields +static const uint32_t kLFHGPBFlags = 6; // general purpose bit flags +static const uint32_t kLFHCRC = 14; // offset to CRC +static const uint32_t kLFHCompLen = 18; // offset to compressed length +static const uint32_t kLFHUncompLen = 22; // offset to uncompressed length +static const uint32_t kLFHNameLen = 26; // offset to filename length +static const uint32_t kLFHExtraLen = 28; // offset to extra length + +static const uint32_t kCDESignature = 0x02014b50; +static const uint32_t kCDELen = 46; // excluding variable-len fields +static const uint32_t kCDEMethod = 10; // offset to compression method +static const uint32_t kCDEModWhen = 12; // offset to modification timestamp +static const uint32_t kCDECRC = 16; // offset to entry CRC +static const uint32_t kCDECompLen = 20; // offset to compressed length +static const uint32_t kCDEUncompLen = 24; // offset to uncompressed length +static const uint32_t kCDENameLen = 28; // offset to filename length +static const uint32_t kCDEExtraLen = 30; // offset to extra length +static const uint32_t kCDECommentLen = 32; // offset to comment length +static const uint32_t kCDELocalOffset = 42; // offset to local hdr + +static const uint32_t kDDOptSignature = 0x08074b50; // *OPTIONAL* data descriptor signature +static const uint32_t kDDSignatureLen = 4; +static const uint32_t kDDLen = 12; +static const uint32_t kDDMaxLen = 16; // max of 16 bytes with a signature, 12 bytes without +static const uint32_t kDDCrc32 = 0; // offset to crc32 +static const uint32_t kDDCompLen = 4; // offset to compressed length +static const uint32_t kDDUncompLen = 8; // offset to uncompressed length + +static const uint32_t kGPBDDFlagMask = 0x0008; // mask value that signifies that the entry has a DD + +static const uint32_t kMaxErrorLen = 1024; + +static const char* kErrorMessages[] = { + "Unknown return code.", + "Iteration ended", + "Zlib error", + "Invalid file", + "Invalid handle", + "Duplicate entries in archive", + "Empty archive", + "Entry not found", + "Invalid offset", + "Inconsistent information", + "Invalid entry name", + "I/O Error", + "File mapping failed" +}; + +static const int32_t kErrorMessageUpperBound = 0; + +static const int32_t kIterationEnd = -1; + +// We encountered a Zlib error when inflating a stream from this file. +// Usually indicates file corruption. +static const int32_t kZlibError = -2; + +// The input file cannot be processed as a zip archive. Usually because +// it's too small, too large or does not have a valid signature. +static const int32_t kInvalidFile = -3; + +// An invalid iteration / ziparchive handle was passed in as an input +// argument. +static const int32_t kInvalidHandle = -4; + +// The zip archive contained two (or possibly more) entries with the same +// name. +static const int32_t kDuplicateEntry = -5; + +// The zip archive contains no entries. +static const int32_t kEmptyArchive = -6; + +// The specified entry was not found in the archive. +static const int32_t kEntryNotFound = -7; + +// The zip archive contained an invalid local file header pointer. +static const int32_t kInvalidOffset = -8; + +// The zip archive contained inconsistent entry information. This could +// be because the central directory & local file header did not agree, or +// if the actual uncompressed length or crc32 do not match their declared +// values. +static const int32_t kInconsistentInformation = -9; + +// An invalid entry name was encountered. +static const int32_t kInvalidEntryName = -10; + +// An I/O related system call (read, lseek, ftruncate, map) failed. +static const int32_t kIoError = -11; + +// We were not able to mmap the central directory or entry contents. +static const int32_t kMmapFailed = -12; + +static const int32_t kErrorMessageLowerBound = -13; + +static const char kTempMappingFileName[] = "zip: ExtractFileToFile"; + +/* + * A Read-only Zip archive. + * + * We want "open" and "find entry by name" to be fast operations, and + * we want to use as little memory as possible. We memory-map the zip + * central directory, and load a hash table with pointers to the filenames + * (which aren't null-terminated). The other fields are at a fixed offset + * from the filename, so we don't need to extract those (but we do need + * to byte-read and endian-swap them every time we want them). + * + * It's possible that somebody has handed us a massive (~1GB) zip archive, + * so we can't expect to mmap the entire file. + * + * To speed comparisons when doing a lookup by name, we could make the mapping + * "private" (copy-on-write) and null-terminate the filenames after verifying + * the record structure. However, this requires a private mapping of + * every page that the Central Directory touches. Easier to tuck a copy + * of the string length into the hash table entry. + */ +struct ZipArchive { + /* open Zip archive */ + int fd; + + /* mapped central directory area */ + off64_t directory_offset; + android::FileMap* directory_map; + + /* number of entries in the Zip archive */ + uint16_t num_entries; + + /* + * We know how many entries are in the Zip archive, so we can have a + * fixed-size hash table. We define a load factor of 0.75 and overallocat + * so the maximum number entries can never be higher than + * ((4 * UINT16_MAX) / 3 + 1) which can safely fit into a uint32_t. + */ + uint32_t hash_table_size; + ZipEntryName* hash_table; +}; + +// Returns 0 on success and negative values on failure. +static android::FileMap* MapFileSegment(const int fd, const off64_t start, + const size_t length, const bool read_only, + const char* debug_file_name) { + android::FileMap* file_map = new android::FileMap; + const bool success = file_map->create(debug_file_name, fd, start, length, read_only); + if (!success) { + file_map->release(); + return NULL; + } + + return file_map; +} + +static int32_t CopyFileToFile(int fd, uint8_t* begin, const uint32_t length, uint64_t *crc_out) { + static const uint32_t kBufSize = 32768; + uint8_t buf[kBufSize]; + + uint32_t count = 0; + uint64_t crc = 0; + while (count < length) { + uint32_t remaining = length - count; + + // Safe conversion because kBufSize is narrow enough for a 32 bit signed + // value. + ssize_t get_size = (remaining > kBufSize) ? kBufSize : remaining; + ssize_t actual = TEMP_FAILURE_RETRY(read(fd, buf, get_size)); + + if (actual != get_size) { + ALOGW("CopyFileToFile: copy read failed (%d vs %zd)", + (int) actual, get_size); + return kIoError; + } + + memcpy(begin + count, buf, get_size); + crc = crc32(crc, buf, get_size); + count += get_size; + } + + *crc_out = crc; + + return 0; +} + +/* + * Round up to the next highest power of 2. + * + * Found on http://graphics.stanford.edu/~seander/bithacks.html. + */ +static uint32_t RoundUpPower2(uint32_t val) { + val--; + val |= val >> 1; + val |= val >> 2; + val |= val >> 4; + val |= val >> 8; + val |= val >> 16; + val++; + + return val; +} + +static uint32_t ComputeHash(const char* str, uint16_t len) { + uint32_t hash = 0; + + while (len--) { + hash = hash * 31 + *str++; + } + + return hash; +} + +/* + * Convert a ZipEntry to a hash table index, verifying that it's in a + * valid range. + */ +static int64_t EntryToIndex(const ZipEntryName* hash_table, + const uint32_t hash_table_size, + const char* name, uint16_t length) { + const uint32_t hash = ComputeHash(name, length); + + // NOTE: (hash_table_size - 1) is guaranteed to be non-negative. + uint32_t ent = hash & (hash_table_size - 1); + while (hash_table[ent].name != NULL) { + if (hash_table[ent].name_length == length && + memcmp(hash_table[ent].name, name, length) == 0) { + return ent; + } + + ent = (ent + 1) & (hash_table_size - 1); + } + + ALOGV("Zip: Unable to find entry %.*s", name_length, name); + return kEntryNotFound; +} + +/* + * Add a new entry to the hash table. + */ +static int32_t AddToHash(ZipEntryName *hash_table, const uint64_t hash_table_size, + const char* name, uint16_t length) { + const uint64_t hash = ComputeHash(name, length); + uint32_t ent = hash & (hash_table_size - 1); + + /* + * We over-allocated the table, so we're guaranteed to find an empty slot. + * Further, we guarantee that the hashtable size is not 0. + */ + while (hash_table[ent].name != NULL) { + if (hash_table[ent].name_length == length && + memcmp(hash_table[ent].name, name, length) == 0) { + // We've found a duplicate entry. We don't accept it + ALOGW("Zip: Found duplicate entry %.*s", length, name); + return kDuplicateEntry; + } + ent = (ent + 1) & (hash_table_size - 1); + } + + hash_table[ent].name = name; + hash_table[ent].name_length = length; + return 0; +} + +/* + * Get 2 little-endian bytes. + */ +static uint16_t get2LE(const uint8_t* src) { + return src[0] | (src[1] << 8); +} + +/* + * Get 4 little-endian bytes. + */ +static uint32_t get4LE(const uint8_t* src) { + uint32_t result; + + result = src[0]; + result |= src[1] << 8; + result |= src[2] << 16; + result |= src[3] << 24; + + return result; +} + +static int32_t MapCentralDirectory0(int fd, const char* debug_file_name, + ZipArchive* archive, off64_t file_length, + uint32_t read_amount, uint8_t* scan_buffer) { + const off64_t search_start = file_length - read_amount; + + if (lseek64(fd, search_start, SEEK_SET) != search_start) { + ALOGW("Zip: seek %lld failed: %s", search_start, strerror(errno)); + return kIoError; + } + ssize_t actual = TEMP_FAILURE_RETRY(read(fd, scan_buffer, read_amount)); + if (actual != (ssize_t) read_amount) { + ALOGW("Zip: read %zd failed: %s", read_amount, strerror(errno)); + return kIoError; + } + + /* + * Scan backward for the EOCD magic. In an archive without a trailing + * comment, we'll find it on the first try. (We may want to consider + * doing an initial minimal read; if we don't find it, retry with a + * second read as above.) + */ + int i; + for (i = read_amount - kEOCDLen; i >= 0; i--) { + if (scan_buffer[i] == 0x50 && get4LE(&scan_buffer[i]) == kEOCDSignature) { + ALOGV("+++ Found EOCD at buf+%d", i); + break; + } + } + if (i < 0) { + ALOGD("Zip: EOCD not found, %s is not zip", debug_file_name); + return kInvalidFile; + } + + const off64_t eocd_offset = search_start + i; + const uint8_t* eocd_ptr = scan_buffer + i; + + assert(eocd_offset < file_length); + + /* + * Grab the CD offset and size, and the number of entries in the + * archive. Verify that they look reasonable. Widen dir_size and + * dir_offset to the file offset type. + */ + const uint16_t num_entries = get2LE(eocd_ptr + kEOCDNumEntries); + const off64_t dir_size = get4LE(eocd_ptr + kEOCDSize); + const off64_t dir_offset = get4LE(eocd_ptr + kEOCDFileOffset); + + if (dir_offset + dir_size > eocd_offset) { + ALOGW("Zip: bad offsets (dir %lld, size %lld, eocd %lld)", + dir_offset, dir_size, eocd_offset); + return kInvalidOffset; + } + if (num_entries == 0) { + ALOGW("Zip: empty archive?"); + return kEmptyArchive; + } + + ALOGV("+++ num_entries=%d dir_size=%d dir_offset=%d", num_entries, dir_size, + dir_offset); + + /* + * It all looks good. Create a mapping for the CD, and set the fields + * in archive. + */ + android::FileMap* map = MapFileSegment(fd, dir_offset, dir_size, + true /* read only */, debug_file_name); + if (map == NULL) { + archive->directory_map = NULL; + return kMmapFailed; + } + + archive->directory_map = map; + archive->num_entries = num_entries; + archive->directory_offset = dir_offset; + + return 0; +} + +/* + * Find the zip Central Directory and memory-map it. + * + * On success, returns 0 after populating fields from the EOCD area: + * directory_offset + * directory_map + * num_entries + */ +static int32_t MapCentralDirectory(int fd, const char* debug_file_name, + ZipArchive* archive) { + + // Test file length. We use lseek64 to make sure the file + // is small enough to be a zip file (Its size must be less than + // 0xffffffff bytes). + off64_t file_length = lseek64(fd, 0, SEEK_END); + if (file_length == -1) { + ALOGV("Zip: lseek on fd %d failed", fd); + return kInvalidFile; + } + + if (file_length > (off64_t) 0xffffffff) { + ALOGV("Zip: zip file too long %d", file_length); + return kInvalidFile; + } + + if (file_length < (int64_t) kEOCDLen) { + ALOGV("Zip: length %ld is too small to be zip", file_length); + return kInvalidFile; + } + + /* + * Perform the traditional EOCD snipe hunt. + * + * We're searching for the End of Central Directory magic number, + * which appears at the start of the EOCD block. It's followed by + * 18 bytes of EOCD stuff and up to 64KB of archive comment. We + * need to read the last part of the file into a buffer, dig through + * it to find the magic number, parse some values out, and use those + * to determine the extent of the CD. + * + * We start by pulling in the last part of the file. + */ + uint32_t read_amount = kMaxEOCDSearch; + if (file_length < (off64_t) read_amount) { + read_amount = file_length; + } + + uint8_t* scan_buffer = (uint8_t*) malloc(read_amount); + int32_t result = MapCentralDirectory0(fd, debug_file_name, archive, + file_length, read_amount, scan_buffer); + + free(scan_buffer); + return result; +} + +/* + * Parses the Zip archive's Central Directory. Allocates and populates the + * hash table. + * + * Returns 0 on success. + */ +static int32_t ParseZipArchive(ZipArchive* archive) { + int32_t result = -1; + const uint8_t* cd_ptr = (const uint8_t*) archive->directory_map->getDataPtr(); + size_t cd_length = archive->directory_map->getDataLength(); + uint16_t num_entries = archive->num_entries; + + /* + * Create hash table. We have a minimum 75% load factor, possibly as + * low as 50% after we round off to a power of 2. There must be at + * least one unused entry to avoid an infinite loop during creation. + */ + archive->hash_table_size = RoundUpPower2(1 + (num_entries * 4) / 3); + archive->hash_table = (ZipEntryName*) calloc(archive->hash_table_size, + sizeof(ZipEntryName)); + + /* + * Walk through the central directory, adding entries to the hash + * table and verifying values. + */ + const uint8_t* ptr = cd_ptr; + for (uint16_t i = 0; i < num_entries; i++) { + if (get4LE(ptr) != kCDESignature) { + ALOGW("Zip: missed a central dir sig (at %d)", i); + goto bail; + } + + if (ptr + kCDELen > cd_ptr + cd_length) { + ALOGW("Zip: ran off the end (at %d)", i); + goto bail; + } + + const off64_t local_header_offset = get4LE(ptr + kCDELocalOffset); + if (local_header_offset >= archive->directory_offset) { + ALOGW("Zip: bad LFH offset %lld at entry %d", local_header_offset, i); + goto bail; + } + + const uint16_t file_name_length = get2LE(ptr + kCDENameLen); + const uint16_t extra_length = get2LE(ptr + kCDEExtraLen); + const uint16_t comment_length = get2LE(ptr + kCDECommentLen); + + /* add the CDE filename to the hash table */ + const int add_result = AddToHash(archive->hash_table, + archive->hash_table_size, (const char*) ptr + kCDELen, file_name_length); + if (add_result) { + ALOGW("Zip: Error adding entry to hash table %d", add_result); + result = add_result; + goto bail; + } + + ptr += kCDELen + file_name_length + extra_length + comment_length; + if ((size_t)(ptr - cd_ptr) > cd_length) { + ALOGW("Zip: bad CD advance (%d vs %zd) at entry %d", + (int) (ptr - cd_ptr), cd_length, i); + goto bail; + } + } + ALOGV("+++ zip good scan %d entries", num_entries); + + result = 0; + +bail: + return result; +} + +static int32_t OpenArchiveInternal(ZipArchive* archive, + const char* debug_file_name) { + int32_t result = -1; + if ((result = MapCentralDirectory(archive->fd, debug_file_name, archive))) { + return result; + } + + if ((result = ParseZipArchive(archive))) { + return result; + } + + return 0; +} + +int32_t OpenArchiveFd(int fd, const char* debug_file_name, + ZipArchiveHandle* handle) { + ZipArchive* archive = (ZipArchive*) malloc(sizeof(ZipArchive)); + memset(archive, 0, sizeof(*archive)); + *handle = archive; + + archive->fd = fd; + + return OpenArchiveInternal(archive, debug_file_name); +} + +int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle) { + ZipArchive* archive = (ZipArchive*) malloc(sizeof(ZipArchive)); + memset(archive, 0, sizeof(*archive)); + *handle = archive; + + const int fd = open(fileName, O_RDONLY | O_BINARY, 0); + if (fd < 0) { + ALOGW("Unable to open '%s': %s", fileName, strerror(errno)); + return kIoError; + } else { + archive->fd = fd; + } + + return OpenArchiveInternal(archive, fileName); +} + +/* + * Close a ZipArchive, closing the file and freeing the contents. + */ +void CloseArchive(ZipArchiveHandle handle) { + ZipArchive* archive = (ZipArchive*) handle; + ALOGV("Closing archive %p", archive); + + if (archive->fd >= 0) { + close(archive->fd); + } + + if (archive->directory_map != NULL) { + archive->directory_map->release(); + } + free(archive->hash_table); + + /* ensure nobody tries to use the ZipArchive after it's closed */ + archive->directory_offset = -1; + archive->fd = -1; + archive->num_entries = -1; + archive->hash_table_size = -1; + archive->hash_table = NULL; +} + +static int32_t UpdateEntryFromDataDescriptor(int fd, + ZipEntry *entry) { + uint8_t ddBuf[kDDMaxLen]; + ssize_t actual = TEMP_FAILURE_RETRY(read(fd, ddBuf, sizeof(ddBuf))); + if (actual != sizeof(ddBuf)) { + return kIoError; + } + + const uint32_t ddSignature = get4LE(ddBuf); + uint16_t ddOffset = 0; + if (ddSignature == kDDOptSignature) { + ddOffset = 4; + } + + entry->crc32 = get4LE(ddBuf + ddOffset + kDDCrc32); + entry->compressed_length = get4LE(ddBuf + ddOffset + kDDCompLen); + entry->uncompressed_length = get4LE(ddBuf + ddOffset + kDDUncompLen); + + return 0; +} + +// Attempts to read |len| bytes into |buf| at offset |off|. +// +// This method uses pread64 on platforms that support it and +// lseek64 + read on platforms that don't. This implies that +// callers should not rely on the |fd| offset being incremented +// as a side effect of this call. +static inline ssize_t ReadAtOffset(int fd, uint8_t* buf, size_t len, + off64_t off) { +#ifdef HAVE_PREAD + return TEMP_FAILURE_RETRY(pread64(fd, buf, len, off)); +#else + // The only supported platform that doesn't support pread at the moment + // is Windows. Only recent versions of windows support unix like forks, + // and even there the semantics are quite different. + if (lseek64(fd, off, SEEK_SET) != off) { + ALOGW("Zip: failed seek to offset %lld", off); + return kIoError; + } + + return TEMP_FAILURE_RETRY(read(fd, buf, len)); +#endif // HAVE_PREAD +} + +static int32_t FindEntry(const ZipArchive* archive, const int ent, + ZipEntry* data) { + const uint16_t nameLen = archive->hash_table[ent].name_length; + const char* name = archive->hash_table[ent].name; + + // Recover the start of the central directory entry from the filename + // pointer. The filename is the first entry past the fixed-size data, + // so we can just subtract back from that. + const unsigned char* ptr = (const unsigned char*) name; + ptr -= kCDELen; + + // This is the base of our mmapped region, we have to sanity check that + // the name that's in the hash table is a pointer to a location within + // this mapped region. + const unsigned char* base_ptr = (const unsigned char*) + archive->directory_map->getDataPtr(); + if (ptr < base_ptr || ptr > base_ptr + archive->directory_map->getDataLength()) { + ALOGW("Zip: Invalid entry pointer"); + return kInvalidOffset; + } + + // The offset of the start of the central directory in the zipfile. + // We keep this lying around so that we can sanity check all our lengths + // and our per-file structures. + const off64_t cd_offset = archive->directory_offset; + + // Fill out the compression method, modification time, crc32 + // and other interesting attributes from the central directory. These + // will later be compared against values from the local file header. + data->method = get2LE(ptr + kCDEMethod); + data->mod_time = get4LE(ptr + kCDEModWhen); + data->crc32 = get4LE(ptr + kCDECRC); + data->compressed_length = get4LE(ptr + kCDECompLen); + data->uncompressed_length = get4LE(ptr + kCDEUncompLen); + + // Figure out the local header offset from the central directory. The + // actual file data will begin after the local header and the name / + // extra comments. + const off64_t local_header_offset = get4LE(ptr + kCDELocalOffset); + if (local_header_offset + (off64_t) kLFHLen >= cd_offset) { + ALOGW("Zip: bad local hdr offset in zip"); + return kInvalidOffset; + } + + uint8_t lfh_buf[kLFHLen]; + ssize_t actual = ReadAtOffset(archive->fd, lfh_buf, sizeof(lfh_buf), + local_header_offset); + if (actual != sizeof(lfh_buf)) { + ALOGW("Zip: failed reading lfh name from offset %lld", local_header_offset); + return kIoError; + } + + if (get4LE(lfh_buf) != kLFHSignature) { + ALOGW("Zip: didn't find signature at start of lfh, offset=%lld", + local_header_offset); + return kInvalidOffset; + } + + // Paranoia: Match the values specified in the local file header + // to those specified in the central directory. + const uint16_t lfhGpbFlags = get2LE(lfh_buf + kLFHGPBFlags); + const uint16_t lfhNameLen = get2LE(lfh_buf + kLFHNameLen); + const uint16_t lfhExtraLen = get2LE(lfh_buf + kLFHExtraLen); + + if ((lfhGpbFlags & kGPBDDFlagMask) == 0) { + const uint32_t lfhCrc = get4LE(lfh_buf + kLFHCRC); + const uint32_t lfhCompLen = get4LE(lfh_buf + kLFHCompLen); + const uint32_t lfhUncompLen = get4LE(lfh_buf + kLFHUncompLen); + + data->has_data_descriptor = 0; + if (data->compressed_length != lfhCompLen || data->uncompressed_length != lfhUncompLen + || data->crc32 != lfhCrc) { + ALOGW("Zip: size/crc32 mismatch. expected {%d, %d, %x}, was {%d, %d, %x}", + data->compressed_length, data->uncompressed_length, data->crc32, + lfhCompLen, lfhUncompLen, lfhCrc); + return kInconsistentInformation; + } + } else { + data->has_data_descriptor = 1; + } + + // Check that the local file header name matches the declared + // name in the central directory. + if (lfhNameLen == nameLen) { + const off64_t name_offset = local_header_offset + kLFHLen; + if (name_offset + lfhNameLen >= cd_offset) { + ALOGW("Zip: Invalid declared length"); + return kInvalidOffset; + } + + uint8_t* name_buf = (uint8_t*) malloc(nameLen); + ssize_t actual = ReadAtOffset(archive->fd, name_buf, nameLen, + name_offset); + + if (actual != nameLen) { + ALOGW("Zip: failed reading lfh name from offset %lld", name_offset); + free(name_buf); + return kIoError; + } + + if (memcmp(name, name_buf, nameLen)) { + free(name_buf); + return kInconsistentInformation; + } + + free(name_buf); + } else { + ALOGW("Zip: lfh name did not match central directory."); + return kInconsistentInformation; + } + + const off64_t data_offset = local_header_offset + kLFHLen + lfhNameLen + lfhExtraLen; + if (data_offset > cd_offset) { + ALOGW("Zip: bad data offset %lld in zip", (off64_t) data_offset); + return kInvalidOffset; + } + + if ((off64_t)(data_offset + data->compressed_length) > cd_offset) { + ALOGW("Zip: bad compressed length in zip (%lld + %zd > %lld)", + data_offset, data->compressed_length, cd_offset); + return kInvalidOffset; + } + + if (data->method == kCompressStored && + (off64_t)(data_offset + data->uncompressed_length) > cd_offset) { + ALOGW("Zip: bad uncompressed length in zip (%lld + %zd > %lld)", + data_offset, data->uncompressed_length, cd_offset); + return kInvalidOffset; + } + + data->offset = data_offset; + return 0; +} + +struct IterationHandle { + uint32_t position; + const char* prefix; + uint16_t prefix_len; + ZipArchive* archive; +}; + +int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr, const char* prefix) { + ZipArchive* archive = (ZipArchive *) handle; + + if (archive == NULL || archive->hash_table == NULL) { + ALOGW("Zip: Invalid ZipArchiveHandle"); + return kInvalidHandle; + } + + IterationHandle* cookie = (IterationHandle*) malloc(sizeof(IterationHandle)); + cookie->position = 0; + cookie->prefix = prefix; + cookie->archive = archive; + if (prefix != NULL) { + cookie->prefix_len = strlen(prefix); + } + + *cookie_ptr = cookie ; + return 0; +} + +int32_t FindEntry(const ZipArchiveHandle handle, const char* entryName, + ZipEntry* data) { + const ZipArchive* archive = (ZipArchive*) handle; + const int nameLen = strlen(entryName); + if (nameLen == 0 || nameLen > 65535) { + ALOGW("Zip: Invalid filename %s", entryName); + return kInvalidEntryName; + } + + const int64_t ent = EntryToIndex(archive->hash_table, + archive->hash_table_size, entryName, nameLen); + + if (ent < 0) { + ALOGV("Zip: Could not find entry %.*s", nameLen, entryName); + return ent; + } + + return FindEntry(archive, ent, data); +} + +int32_t Next(void* cookie, ZipEntry* data, ZipEntryName* name) { + IterationHandle* handle = (IterationHandle *) cookie; + if (handle == NULL) { + return kInvalidHandle; + } + + ZipArchive* archive = handle->archive; + if (archive == NULL || archive->hash_table == NULL) { + ALOGW("Zip: Invalid ZipArchiveHandle"); + return kInvalidHandle; + } + + const uint32_t currentOffset = handle->position; + const uint32_t hash_table_length = archive->hash_table_size; + const ZipEntryName *hash_table = archive->hash_table; + + for (uint32_t i = currentOffset; i < hash_table_length; ++i) { + if (hash_table[i].name != NULL && + (handle->prefix == NULL || + (memcmp(handle->prefix, hash_table[i].name, handle->prefix_len) == 0))) { + handle->position = (i + 1); + const int error = FindEntry(archive, i, data); + if (!error) { + name->name = hash_table[i].name; + name->name_length = hash_table[i].name_length; + } + + return error; + } + } + + handle->position = 0; + return kIterationEnd; +} + +static int32_t InflateToFile(int fd, const ZipEntry* entry, + uint8_t* begin, uint32_t length, + uint64_t* crc_out) { + int32_t result = -1; + const uint32_t kBufSize = 32768; + uint8_t read_buf[kBufSize]; + uint8_t write_buf[kBufSize]; + z_stream zstream; + int zerr; + + /* + * Initialize the zlib stream struct. + */ + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.next_in = NULL; + zstream.avail_in = 0; + zstream.next_out = (Bytef*) write_buf; + zstream.avail_out = kBufSize; + zstream.data_type = Z_UNKNOWN; + + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ + zerr = inflateInit2(&zstream, -MAX_WBITS); + if (zerr != Z_OK) { + if (zerr == Z_VERSION_ERROR) { + ALOGE("Installed zlib is not compatible with linked version (%s)", + ZLIB_VERSION); + } else { + ALOGW("Call to inflateInit2 failed (zerr=%d)", zerr); + } + + return kZlibError; + } + + const uint32_t uncompressed_length = entry->uncompressed_length; + + uint32_t compressed_length = entry->compressed_length; + uint32_t write_count = 0; + do { + /* read as much as we can */ + if (zstream.avail_in == 0) { + const ssize_t getSize = (compressed_length > kBufSize) ? kBufSize : compressed_length; + const ssize_t actual = TEMP_FAILURE_RETRY(read(fd, read_buf, getSize)); + if (actual != getSize) { + ALOGW("Zip: inflate read failed (%d vs %zd)", actual, getSize); + result = kIoError; + goto z_bail; + } + + compressed_length -= getSize; + + zstream.next_in = read_buf; + zstream.avail_in = getSize; + } + + /* uncompress the data */ + zerr = inflate(&zstream, Z_NO_FLUSH); + if (zerr != Z_OK && zerr != Z_STREAM_END) { + ALOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)", + zerr, zstream.next_in, zstream.avail_in, + zstream.next_out, zstream.avail_out); + result = kZlibError; + goto z_bail; + } + + /* write when we're full or when we're done */ + if (zstream.avail_out == 0 || + (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) { + const size_t write_size = zstream.next_out - write_buf; + // The file might have declared a bogus length. + if (write_size + write_count > length) { + goto z_bail; + } + memcpy(begin + write_count, write_buf, write_size); + write_count += write_size; + + zstream.next_out = write_buf; + zstream.avail_out = kBufSize; + } + } while (zerr == Z_OK); + + assert(zerr == Z_STREAM_END); /* other errors should've been caught */ + + // stream.adler holds the crc32 value for such streams. + *crc_out = zstream.adler; + + if (zstream.total_out != uncompressed_length || compressed_length != 0) { + ALOGW("Zip: size mismatch on inflated file (%ld vs %zd)", + zstream.total_out, uncompressed_length); + result = kInconsistentInformation; + goto z_bail; + } + + result = 0; + +z_bail: + inflateEnd(&zstream); /* free up any allocated structures */ + + return result; +} + +int32_t ExtractToMemory(ZipArchiveHandle handle, + ZipEntry* entry, uint8_t* begin, uint32_t size) { + ZipArchive* archive = (ZipArchive*) handle; + const uint16_t method = entry->method; + off64_t data_offset = entry->offset; + + if (lseek64(archive->fd, data_offset, SEEK_SET) != data_offset) { + ALOGW("Zip: lseek to data at %lld failed", (off64_t) data_offset); + return kIoError; + } + + // this should default to kUnknownCompressionMethod. + int32_t return_value = -1; + uint64_t crc = 0; + if (method == kCompressStored) { + return_value = CopyFileToFile(archive->fd, begin, size, &crc); + } else if (method == kCompressDeflated) { + return_value = InflateToFile(archive->fd, entry, begin, size, &crc); + } + + if (!return_value && entry->has_data_descriptor) { + return_value = UpdateEntryFromDataDescriptor(archive->fd, entry); + if (return_value) { + return return_value; + } + } + + // TODO: Fix this check by passing the right flags to inflate2 so that + // it calculates the CRC for us. + if (entry->crc32 != crc && false) { + ALOGW("Zip: crc mismatch: expected %u, was %llu", entry->crc32, crc); + return kInconsistentInformation; + } + + return return_value; +} + +int32_t ExtractEntryToFile(ZipArchiveHandle handle, + ZipEntry* entry, int fd) { + const int32_t declared_length = entry->uncompressed_length; + + const off64_t current_offset = lseek64(fd, 0, SEEK_CUR); + if (current_offset == -1) { + ALOGW("Zip: unable to seek to current location on fd %d: %s", fd, + strerror(errno)); + return kIoError; + } + + int result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset)); + if (result == -1) { + ALOGW("Zip: unable to truncate file to %lld: %s", declared_length + current_offset, + strerror(errno)); + return kIoError; + } + + // Don't attempt to map a region of length 0. We still need the + // ftruncate() though, since the API guarantees that we will truncate + // the file to the end of the uncompressed output. + if (declared_length == 0) { + return 0; + } + + android::FileMap* map = MapFileSegment(fd, current_offset, declared_length, + false, kTempMappingFileName); + if (map == NULL) { + return kMmapFailed; + } + + const int32_t error = ExtractToMemory(handle, entry, + reinterpret_cast<uint8_t*>(map->getDataPtr()), + map->getDataLength()); + map->release(); + return error; +} + +const char* ErrorCodeString(int32_t error_code) { + if (error_code > kErrorMessageLowerBound && error_code < kErrorMessageUpperBound) { + return kErrorMessages[error_code * -1]; + } + + return kErrorMessages[0]; +} + +int GetFileDescriptor(const ZipArchiveHandle handle) { + return ((ZipArchive*) handle)->fd; +} + diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc new file mode 100644 index 0000000..3082216 --- /dev/null +++ b/libziparchive/zip_archive_test.cc @@ -0,0 +1,250 @@ +/* + * 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. + */ + +#include "ziparchive/zip_archive.h" + +#include <errno.h> +#include <getopt.h> +#include <stdio.h> +#include <unistd.h> +#include <vector> + +#include <gtest/gtest.h> + +static std::string test_data_dir; + +static const std::string kValidZip = "valid.zip"; + +static const uint8_t kATxtContents[] = { + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', + '\n' +}; + +static const uint8_t kBTxtContents[] = { + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', + '\n' +}; + +static int32_t OpenArchiveWrapper(const std::string& name, + ZipArchiveHandle* handle) { + const std::string abs_path = test_data_dir + "/" + name; + return OpenArchive(abs_path.c_str(), handle); +} + +static void AssertNameEquals(const std::string& name_str, + const ZipEntryName& name) { + ASSERT_EQ(name_str.size(), name.name_length); + ASSERT_EQ(0, memcmp(name_str.c_str(), name.name, name.name_length)); +} + +TEST(ziparchive, Open) { + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle)); + + CloseArchive(handle); +} + +TEST(ziparchive, Iteration) { + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle)); + + void* iteration_cookie; + ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL)); + + ZipEntry data; + ZipEntryName name; + + // b/c.txt + ASSERT_EQ(0, Next(iteration_cookie, &data, &name)); + AssertNameEquals("b/c.txt", name); + + // b/d.txt + ASSERT_EQ(0, Next(iteration_cookie, &data, &name)); + AssertNameEquals("b/d.txt", name); + + // a.txt + ASSERT_EQ(0, Next(iteration_cookie, &data, &name)); + AssertNameEquals("a.txt", name); + + // b.txt + ASSERT_EQ(0, Next(iteration_cookie, &data, &name)); + AssertNameEquals("b.txt", name); + + // b/ + ASSERT_EQ(0, Next(iteration_cookie, &data, &name)); + AssertNameEquals("b/", name); + + // End of iteration. + ASSERT_EQ(-1, Next(iteration_cookie, &data, &name)); + + CloseArchive(handle); +} + +TEST(ziparchive, FindEntry) { + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle)); + + ZipEntry data; + ASSERT_EQ(0, FindEntry(handle, "a.txt", &data)); + + // Known facts about a.txt, from zipinfo -v. + ASSERT_EQ(63, data.offset); + ASSERT_EQ(kCompressDeflated, data.method); + ASSERT_EQ(static_cast<uint32_t>(17), data.uncompressed_length); + ASSERT_EQ(static_cast<uint32_t>(13), data.compressed_length); + ASSERT_EQ(0x950821c5, data.crc32); + + // An entry that doesn't exist. Should be a negative return code. + ASSERT_LT(FindEntry(handle, "nonexistent.txt", &data), 0); + + CloseArchive(handle); +} + +TEST(ziparchive, ExtractToMemory) { + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle)); + + // An entry that's deflated. + ZipEntry data; + ASSERT_EQ(0, FindEntry(handle, "a.txt", &data)); + const uint32_t a_size = data.uncompressed_length; + ASSERT_EQ(a_size, sizeof(kATxtContents)); + uint8_t* buffer = new uint8_t[a_size]; + ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, a_size)); + ASSERT_EQ(0, memcmp(buffer, kATxtContents, a_size)); + delete[] buffer; + + // An entry that's stored. + ASSERT_EQ(0, FindEntry(handle, "b.txt", &data)); + const uint32_t b_size = data.uncompressed_length; + ASSERT_EQ(b_size, sizeof(kBTxtContents)); + buffer = new uint8_t[b_size]; + ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, b_size)); + ASSERT_EQ(0, memcmp(buffer, kBTxtContents, b_size)); + delete[] buffer; + + CloseArchive(handle); +} + +TEST(ziparchive, EmptyEntries) { + char temp_file_pattern[] = "empty_entries_test_XXXXXX"; + int fd = mkstemp(temp_file_pattern); + ASSERT_NE(-1, fd); + const uint32_t data[] = { + 0x04034b50, 0x0000000a, 0x63600000, 0x00004438, 0x00000000, 0x00000000, + 0x00090000, 0x6d65001c, 0x2e797470, 0x55747874, 0x03000954, 0x52e25c13, + 0x52e25c24, 0x000b7875, 0x42890401, 0x88040000, 0x50000013, 0x1e02014b, + 0x00000a03, 0x60000000, 0x00443863, 0x00000000, 0x00000000, 0x09000000, + 0x00001800, 0x00000000, 0xa0000000, 0x00000081, 0x706d6500, 0x742e7974, + 0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904, 0x13880400, + 0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000 }; + const ssize_t file_size = 168; + ASSERT_EQ(file_size, TEMP_FAILURE_RETRY(write(fd, data, file_size))); + + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchiveFd(fd, "EmptyEntriesTest", &handle)); + + ZipEntry entry; + ASSERT_EQ(0, FindEntry(handle, "empty.txt", &entry)); + ASSERT_EQ(static_cast<uint32_t>(0), entry.uncompressed_length); + uint8_t buffer[1]; + ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1)); + + char output_file_pattern[] = "empty_entries_output_XXXXXX"; + int output_fd = mkstemp(output_file_pattern); + ASSERT_NE(-1, output_fd); + ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, output_fd)); + + struct stat stat_buf; + ASSERT_EQ(0, fstat(output_fd, &stat_buf)); + ASSERT_EQ(0, stat_buf.st_size); + + close(fd); + close(output_fd); +} + +TEST(ziparchive, ExtractToFile) { + char kTempFilePattern[] = "zip_archive_input_XXXXXX"; + int fd = mkstemp(kTempFilePattern); + ASSERT_NE(-1, fd); + const uint8_t data[8] = { '1', '2', '3', '4', '5', '6', '7', '8' }; + const ssize_t data_size = sizeof(data); + + ASSERT_EQ(data_size, TEMP_FAILURE_RETRY(write(fd, data, data_size))); + + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle)); + + ZipEntry entry; + ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry)); + ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, fd)); + + + // Assert that the first 8 bytes of the file haven't been clobbered. + uint8_t read_buffer[data_size]; + ASSERT_EQ(0, lseek64(fd, 0, SEEK_SET)); + ASSERT_EQ(data_size, TEMP_FAILURE_RETRY(read(fd, read_buffer, data_size))); + ASSERT_EQ(0, memcmp(read_buffer, data, data_size)); + + // Assert that the remainder of the file contains the incompressed data. + std::vector<uint8_t> uncompressed_data(entry.uncompressed_length); + ASSERT_EQ(static_cast<ssize_t>(entry.uncompressed_length), + TEMP_FAILURE_RETRY( + read(fd, &uncompressed_data[0], entry.uncompressed_length))); + ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents, + sizeof(kATxtContents))); + + // Assert that the total length of the file is sane + ASSERT_EQ(data_size + sizeof(kATxtContents), lseek64(fd, 0, SEEK_END)); + + close(fd); +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + + static struct option options[] = { + { "test_data_dir", required_argument, NULL, 't' }, + { NULL, 0, NULL, 0 } + }; + + while (true) { + int option_index; + const int c = getopt_long_only(argc, argv, "", options, &option_index); + if (c == -1) { + break; + } + + if (c == 't') { + test_data_dir = optarg; + } + } + + if (test_data_dir.size() == 0) { + printf("Test data flag (--test_data_dir) required\n\n"); + return -1; + } + + if (test_data_dir[0] != '/') { + printf("Test data must be an absolute path, was %s\n\n", + test_data_dir.c_str()); + return -2; + } + + return RUN_ALL_TESTS(); +} + diff --git a/lmkd/Android.mk b/lmkd/Android.mk new file mode 100644 index 0000000..5d6d1d2 --- /dev/null +++ b/lmkd/Android.mk @@ -0,0 +1,10 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := lmkd.c +LOCAL_STATIC_LIBRARIES := libcutils liblog libm libc +LOCAL_FORCE_STATIC_EXECUTABLE := true + +LOCAL_MODULE := lmkd + +include $(BUILD_EXECUTABLE) diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c new file mode 100644 index 0000000..376410b --- /dev/null +++ b/lmkd/lmkd.c @@ -0,0 +1,735 @@ +/* + * 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. + */ + +#define LOG_TAG "lowmemorykiller" + +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <arpa/inet.h> +#include <sys/epoll.h> +#include <sys/eventfd.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <cutils/log.h> +#include <cutils/sockets.h> + +#define MEMCG_SYSFS_PATH "/dev/memcg/" +#define MEMPRESSURE_WATCH_LEVEL "medium" +#define ZONEINFO_PATH "/proc/zoneinfo" +#define LINE_MAX 128 + +#define INKERNEL_MINFREE_PATH "/sys/module/lowmemorykiller/parameters/minfree" +#define INKERNEL_ADJ_PATH "/sys/module/lowmemorykiller/parameters/adj" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) + +enum lmk_cmd { + LMK_TARGET, + LMK_PROCPRIO, + LMK_PROCREMOVE, +}; + +#define MAX_TARGETS 6 +/* + * longest is LMK_TARGET followed by MAX_TARGETS each minfree and minkillprio + * values + */ +#define CTRL_PACKET_MAX (sizeof(int) * (MAX_TARGETS * 2 + 1)) + +/* default to old in-kernel interface if no memory pressure events */ +static int use_inkernel_interface = 1; + +/* memory pressure level medium event */ +static int mpevfd; + +/* control socket listen and data */ +static int ctrl_lfd; +static int ctrl_dfd = -1; +static int ctrl_dfd_reopened; /* did we reopen ctrl conn on this loop? */ + +/* 1 memory pressure level, 1 ctrl listen socket, 1 ctrl data socket */ +#define MAX_EPOLL_EVENTS 3 +static int epollfd; +static int maxevents; + +#define OOM_DISABLE (-17) +/* inclusive */ +#define OOM_ADJUST_MIN (-16) +#define OOM_ADJUST_MAX 15 + +static int lowmem_adj[MAX_TARGETS]; +static int lowmem_minfree[MAX_TARGETS]; +static int lowmem_targets_size; + +struct sysmeminfo { + int nr_free_pages; + int nr_file_pages; + int nr_shmem; + int totalreserve_pages; +}; + +struct adjslot_list { + struct adjslot_list *next; + struct adjslot_list *prev; +}; + +struct proc { + struct adjslot_list asl; + int pid; + int oomadj; + struct proc *pidhash_next; +}; + +#define PIDHASH_SZ 1024 +static struct proc *pidhash[PIDHASH_SZ]; +#define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1)) + +#define ADJTOSLOT(adj) (adj + -OOM_ADJUST_MIN) +static struct adjslot_list procadjslot_list[ADJTOSLOT(OOM_ADJUST_MAX) + 1]; + +/* + * Wait 1-2 seconds for the death report of a killed process prior to + * considering killing more processes. + */ +#define KILL_TIMEOUT 2 +/* Time of last process kill we initiated, stop me before I kill again */ +static time_t kill_lasttime; + +/* PAGE_SIZE / 1024 */ +static long page_k; + +static struct proc *pid_lookup(int pid) { + struct proc *procp; + + for (procp = pidhash[pid_hashfn(pid)]; procp && procp->pid != pid; + procp = procp->pidhash_next) + ; + + return procp; +} + +static void adjslot_insert(struct adjslot_list *head, struct adjslot_list *new) +{ + struct adjslot_list *next = head->next; + new->prev = head; + new->next = next; + next->prev = new; + head->next = new; +} + +static void adjslot_remove(struct adjslot_list *old) +{ + struct adjslot_list *prev = old->prev; + struct adjslot_list *next = old->next; + next->prev = prev; + prev->next = next; +} + +static struct adjslot_list *adjslot_tail(struct adjslot_list *head) { + struct adjslot_list *asl = head->prev; + + return asl == head ? NULL : asl; +} + +static void proc_slot(struct proc *procp) { + int adjslot = ADJTOSLOT(procp->oomadj); + + adjslot_insert(&procadjslot_list[adjslot], &procp->asl); +} + +static void proc_unslot(struct proc *procp) { + adjslot_remove(&procp->asl); +} + +static void proc_insert(struct proc *procp) { + int hval = pid_hashfn(procp->pid); + + procp->pidhash_next = pidhash[hval]; + pidhash[hval] = procp; + proc_slot(procp); +} + +static int pid_remove(int pid) { + int hval = pid_hashfn(pid); + struct proc *procp; + struct proc *prevp; + + for (procp = pidhash[hval], prevp = NULL; procp && procp->pid != pid; + procp = procp->pidhash_next) + prevp = procp; + + if (!procp) + return -1; + + if (!prevp) + pidhash[hval] = procp->pidhash_next; + else + prevp->pidhash_next = procp->pidhash_next; + + proc_unslot(procp); + free(procp); + return 0; +} + +static void writefilestring(char *path, char *s) { + int fd = open(path, O_WRONLY); + int len = strlen(s); + int ret; + + if (fd < 0) { + ALOGE("Error opening %s; errno=%d", path, errno); + return; + } + + ret = write(fd, s, len); + if (ret < 0) { + ALOGE("Error writing %s; errno=%d", path, errno); + } else if (ret < len) { + ALOGE("Short write on %s; length=%d", path, ret); + } + + close(fd); +} + +static void cmd_procprio(int pid, int oomadj) { + struct proc *procp; + char path[80]; + char val[20]; + + if (oomadj < OOM_DISABLE || oomadj > OOM_ADJUST_MAX) { + ALOGE("Invalid PROCPRIO oomadj argument %d", oomadj); + return; + } + + snprintf(path, sizeof(path), "/proc/%d/oom_adj", pid); + snprintf(val, sizeof(val), "%d", oomadj); + writefilestring(path, val); + + if (use_inkernel_interface) + return; + + procp = pid_lookup(pid); + if (!procp) { + procp = malloc(sizeof(struct proc)); + if (!procp) { + // Oh, the irony. May need to rebuild our state. + return; + } + + procp->pid = pid; + procp->oomadj = oomadj; + proc_insert(procp); + } else { + proc_unslot(procp); + procp->oomadj = oomadj; + proc_slot(procp); + } +} + +static void cmd_procremove(int pid) { + struct proc *procp; + + if (use_inkernel_interface) + return; + + pid_remove(pid); + kill_lasttime = 0; +} + +static void cmd_target(int ntargets, int *params) { + int i; + + if (ntargets > (int)ARRAY_SIZE(lowmem_adj)) + return; + + for (i = 0; i < ntargets; i++) { + lowmem_minfree[i] = ntohl(*params++); + lowmem_adj[i] = ntohl(*params++); + } + + lowmem_targets_size = ntargets; + + if (use_inkernel_interface) { + char minfreestr[128]; + char killpriostr[128]; + + minfreestr[0] = '\0'; + killpriostr[0] = '\0'; + + for (i = 0; i < lowmem_targets_size; i++) { + char val[40]; + + if (i) { + strlcat(minfreestr, ",", sizeof(minfreestr)); + strlcat(killpriostr, ",", sizeof(killpriostr)); + } + + snprintf(val, sizeof(val), "%d", lowmem_minfree[i]); + strlcat(minfreestr, val, sizeof(minfreestr)); + snprintf(val, sizeof(val), "%d", lowmem_adj[i]); + strlcat(killpriostr, val, sizeof(killpriostr)); + } + + writefilestring(INKERNEL_MINFREE_PATH, minfreestr); + writefilestring(INKERNEL_ADJ_PATH, killpriostr); + } +} + +static void ctrl_data_close(void) { + ALOGI("Closing Activity Manager data connection"); + close(ctrl_dfd); + ctrl_dfd = -1; + maxevents--; +} + +static int ctrl_data_read(char *buf, size_t bufsz) { + int ret = 0; + + ret = read(ctrl_dfd, buf, bufsz); + + if (ret == -1) { + ALOGE("control data socket read failed; errno=%d", errno); + } else if (ret == 0) { + ALOGE("Got EOF on control data socket"); + ret = -1; + } + + return ret; +} + +static void ctrl_command_handler(void) { + int ibuf[CTRL_PACKET_MAX / sizeof(int)]; + int len; + int cmd = -1; + int nargs; + int targets; + + len = ctrl_data_read((char *)ibuf, CTRL_PACKET_MAX); + if (len <= 0) + return; + + nargs = len / sizeof(int) - 1; + if (nargs < 0) + goto wronglen; + + cmd = ntohl(ibuf[0]); + + switch(cmd) { + case LMK_TARGET: + targets = nargs / 2; + if (nargs & 0x1 || targets > (int)ARRAY_SIZE(lowmem_adj)) + goto wronglen; + cmd_target(targets, &ibuf[1]); + break; + case LMK_PROCPRIO: + if (nargs != 2) + goto wronglen; + cmd_procprio(ntohl(ibuf[1]), ntohl(ibuf[2])); + break; + case LMK_PROCREMOVE: + if (nargs != 1) + goto wronglen; + cmd_procremove(ntohl(ibuf[1])); + break; + default: + ALOGE("Received unknown command code %d", cmd); + return; + } + + return; + +wronglen: + ALOGE("Wrong control socket read length cmd=%d len=%d", cmd, len); +} + +static void ctrl_data_handler(uint32_t events) { + if (events & EPOLLHUP) { + ALOGI("ActivityManager disconnected"); + if (!ctrl_dfd_reopened) + ctrl_data_close(); + } else if (events & EPOLLIN) { + ctrl_command_handler(); + } +} + +static void ctrl_connect_handler(uint32_t events) { + struct sockaddr addr; + socklen_t alen; + struct epoll_event epev; + + if (ctrl_dfd >= 0) { + ctrl_data_close(); + ctrl_dfd_reopened = 1; + } + + alen = sizeof(addr); + ctrl_dfd = accept(ctrl_lfd, &addr, &alen); + + if (ctrl_dfd < 0) { + ALOGE("lmkd control socket accept failed; errno=%d", errno); + return; + } + + ALOGI("ActivityManager connected"); + maxevents++; + epev.events = EPOLLIN; + epev.data.ptr = (void *)ctrl_data_handler; + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_dfd, &epev) == -1) { + ALOGE("epoll_ctl for data connection socket failed; errno=%d", errno); + ctrl_data_close(); + return; + } +} + +static int zoneinfo_parse_protection(char *cp) { + int max = 0; + int zoneval; + + if (*cp++ != '(') + return 0; + + do { + zoneval = strtol(cp, &cp, 0); + if ((*cp != ',') && (*cp != ')')) + return 0; + if (zoneval > max) + max = zoneval; + } while (cp = strtok(NULL, " ")); + + return max; +} + +static void zoneinfo_parse_line(char *line, struct sysmeminfo *mip) { + char *cp = line; + char *ap; + + cp = strtok(line, " "); + if (!cp) + return; + + ap = strtok(NULL, " "); + if (!ap) + return; + + if (!strcmp(cp, "nr_free_pages")) + mip->nr_free_pages += strtol(ap, NULL, 0); + else if (!strcmp(cp, "nr_file_pages")) + mip->nr_file_pages += strtol(ap, NULL, 0); + else if (!strcmp(cp, "nr_shmem")) + mip->nr_shmem += strtol(ap, NULL, 0); + else if (!strcmp(cp, "high")) + mip->totalreserve_pages += strtol(ap, NULL, 0); + else if (!strcmp(cp, "protection:")) + mip->totalreserve_pages += zoneinfo_parse_protection(ap); +} + +static int zoneinfo_parse(struct sysmeminfo *mip) { + FILE *f; + char *cp; + char line[LINE_MAX]; + + memset(mip, 0, sizeof(struct sysmeminfo)); + f = fopen(ZONEINFO_PATH, "r"); + if (!f) { + ALOGE("%s open: errno=%d", ZONEINFO_PATH, errno); + return -1; + } + + while (fgets(line, LINE_MAX, f)) + zoneinfo_parse_line(line, mip); + + fclose(f); + return 0; +} + +static int proc_get_size(int pid) { + char path[PATH_MAX]; + char line[LINE_MAX]; + FILE *f; + int rss = 0; + int total; + + snprintf(path, PATH_MAX, "/proc/%d/statm", pid); + f = fopen(path, "r"); + if (!f) + return -1; + if (!fgets(line, LINE_MAX, f)) { + fclose(f); + return -1; + } + + sscanf(line, "%d %d ", &total, &rss); + fclose(f); + return rss; +} + +static char *proc_get_name(int pid) { + char path[PATH_MAX]; + static char line[LINE_MAX]; + FILE *f; + char *cp; + + snprintf(path, PATH_MAX, "/proc/%d/cmdline", pid); + f = fopen(path, "r"); + if (!f) + return NULL; + if (!fgets(line, LINE_MAX, f)) { + fclose(f); + return NULL; + } + + cp = strchr(line, ' '); + if (cp) + *cp = '\0'; + + return line; +} + +static struct proc *proc_adj_lru(int oomadj) { + return (struct proc *)adjslot_tail(&procadjslot_list[ADJTOSLOT(oomadj)]); +} + +static void mp_event(uint32_t events) { + int i; + int ret; + unsigned long long evcount; + struct sysmeminfo mi; + int other_free; + int other_file; + int minfree = 0; + int min_score_adj = OOM_ADJUST_MAX + 1; + + ret = read(mpevfd, &evcount, sizeof(evcount)); + if (ret < 0) + ALOGE("Error reading memory pressure event fd; errno=%d", + errno); + + if (time(NULL) - kill_lasttime < KILL_TIMEOUT) + return; + + if (zoneinfo_parse(&mi) < 0) + return; + + other_free = mi.nr_free_pages - mi.totalreserve_pages; + other_file = mi.nr_file_pages - mi.nr_shmem; + + for (i = 0; i < lowmem_targets_size; i++) { + minfree = lowmem_minfree[i]; + if (other_free < minfree && other_file < minfree) { + min_score_adj = lowmem_adj[i]; + break; + } + } + + if (min_score_adj == OOM_ADJUST_MAX + 1) + return; + + for (i = OOM_ADJUST_MAX; i >= min_score_adj; i--) { + struct proc *procp; + + retry: + procp = proc_adj_lru(i); + + if (procp) { + int pid = procp->pid; + char *taskname; + int tasksize; + int r; + + taskname = proc_get_name(pid); + if (!taskname) { + pid_remove(pid); + goto retry; + } + + tasksize = proc_get_size(pid); + if (tasksize < 0) { + pid_remove(pid); + goto retry; + } + + ALOGI("Killing '%s' (%d), adj %d\n" + " to free %ldkB because cache %ldkB is below limit %ldkB for oom_adj %d\n" + " Free memory is %ldkB %s reserved", + taskname, pid, procp->oomadj, tasksize * page_k, + other_file * page_k, minfree * page_k, min_score_adj, + other_free * page_k, other_free >= 0 ? "above" : "below"); + r = kill(pid, SIGKILL); + pid_remove(pid); + + if (r) { + ALOGE("kill(%d): errno=%d", procp->pid, errno); + goto retry; + } else { + time(&kill_lasttime); + break; + } + } + } +} + +static int init_mp(char *levelstr, void *event_handler) +{ + int mpfd; + int evfd; + int evctlfd; + char buf[256]; + struct epoll_event epev; + int ret; + + mpfd = open(MEMCG_SYSFS_PATH "memory.pressure_level", O_RDONLY); + if (mpfd < 0) { + ALOGI("No kernel memory.pressure_level support (errno=%d)", errno); + goto err_open_mpfd; + } + + evctlfd = open(MEMCG_SYSFS_PATH "cgroup.event_control", O_WRONLY); + if (evctlfd < 0) { + ALOGI("No kernel memory cgroup event control (errno=%d)", errno); + goto err_open_evctlfd; + } + + evfd = eventfd(0, EFD_NONBLOCK); + if (evfd < 0) { + ALOGE("eventfd failed for level %s; errno=%d", levelstr, errno); + goto err_eventfd; + } + + ret = snprintf(buf, sizeof(buf), "%d %d %s", evfd, mpfd, levelstr); + if (ret >= (ssize_t)sizeof(buf)) { + ALOGE("cgroup.event_control line overflow for level %s", levelstr); + goto err; + } + + ret = write(evctlfd, buf, strlen(buf) + 1); + if (ret == -1) { + ALOGE("cgroup.event_control write failed for level %s; errno=%d", + levelstr, errno); + goto err; + } + + epev.events = EPOLLIN; + epev.data.ptr = event_handler; + ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, evfd, &epev); + if (ret == -1) { + ALOGE("epoll_ctl for level %s failed; errno=%d", levelstr, errno); + goto err; + } + maxevents++; + mpevfd = evfd; + return 0; + +err: + close(evfd); +err_eventfd: + close(evctlfd); +err_open_evctlfd: + close(mpfd); +err_open_mpfd: + return -1; +} + +static int init(void) { + struct epoll_event epev; + int i; + int ret; + + page_k = sysconf(_SC_PAGESIZE); + if (page_k == -1) + page_k = PAGE_SIZE; + page_k /= 1024; + + epollfd = epoll_create(MAX_EPOLL_EVENTS); + if (epollfd == -1) { + ALOGE("epoll_create failed (errno=%d)", errno); + return -1; + } + + ctrl_lfd = android_get_control_socket("lmkd"); + if (ctrl_lfd < 0) { + ALOGE("get lmkd control socket failed"); + return -1; + } + + ret = listen(ctrl_lfd, 1); + if (ret < 0) { + ALOGE("lmkd control socket listen failed (errno=%d)", errno); + return -1; + } + + epev.events = EPOLLIN; + epev.data.ptr = (void *)ctrl_connect_handler; + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_lfd, &epev) == -1) { + ALOGE("epoll_ctl for lmkd control socket failed (errno=%d)", errno); + return -1; + } + maxevents++; + + use_inkernel_interface = !access(INKERNEL_MINFREE_PATH, W_OK); + + if (use_inkernel_interface) { + ALOGI("Using in-kernel low memory killer interface"); + } else { + ret = init_mp(MEMPRESSURE_WATCH_LEVEL, (void *)&mp_event); + if (ret) + ALOGE("Kernel does not support memory pressure events or in-kernel low memory killer"); + } + + for (i = 0; i <= ADJTOSLOT(OOM_ADJUST_MAX); i++) { + procadjslot_list[i].next = &procadjslot_list[i]; + procadjslot_list[i].prev = &procadjslot_list[i]; + } + + return 0; +} + +static void mainloop(void) { + while (1) { + struct epoll_event events[maxevents]; + int nevents; + int i; + + ctrl_dfd_reopened = 0; + nevents = epoll_wait(epollfd, events, maxevents, -1); + + if (nevents == -1) { + if (errno == EINTR) + continue; + ALOGE("epoll_wait failed (errno=%d)", errno); + continue; + } + + for (i = 0; i < nevents; ++i) { + if (events[i].events & EPOLLERR) + ALOGD("EPOLLERR on event #%d", i); + if (events[i].data.ptr) + (*(void (*)(uint32_t))events[i].data.ptr)(events[i].events); + } + } +} + +int main(int argc, char **argv) { + if (!init()) + mainloop(); + + ALOGI("exiting"); + return 0; +} diff --git a/logcat/Android.mk b/logcat/Android.mk index 7b8eb01..b5e27eb 100644 --- a/logcat/Android.mk +++ b/logcat/Android.mk @@ -1,4 +1,4 @@ -# Copyright 2006 The Android Open Source Project +# Copyright 2006-2014 The Android Open Source Project LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) @@ -10,3 +10,5 @@ LOCAL_SHARED_LIBRARIES := liblog LOCAL_MODULE:= logcat include $(BUILD_EXECUTABLE) + +include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp index d44c679..3c33938 100644 --- a/logcat/logcat.cpp +++ b/logcat/logcat.cpp @@ -1,88 +1,52 @@ -// Copyright 2006 The Android Open Source Project - -#include <log/logger.h> -#include <log/logd.h> -#include <log/logprint.h> -#include <log/event_tag_map.h> -#include <cutils/sockets.h> +// Copyright 2006-2014 The Android Open Source Project +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <string.h> -#include <unistd.h> -#include <fcntl.h> +#include <signal.h> #include <time.h> -#include <errno.h> -#include <assert.h> -#include <ctype.h> +#include <unistd.h> #include <sys/socket.h> #include <sys/stat.h> #include <arpa/inet.h> +#include <cutils/sockets.h> +#include <log/log.h> +#include <log/logger.h> +#include <log/logd.h> +#include <log/logprint.h> +#include <log/event_tag_map.h> + #define DEFAULT_LOG_ROTATE_SIZE_KBYTES 16 #define DEFAULT_MAX_ROTATED_LOGS 4 static AndroidLogFormat * g_logformat; -static bool g_nonblock = false; -static int g_tail_lines = 0; /* logd prefixes records with a length field */ #define RECORD_LENGTH_FIELD_SIZE_BYTES sizeof(uint32_t) -#define LOG_FILE_DIR "/dev/log/" - -struct queued_entry_t { - union { - unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4))); - struct logger_entry entry __attribute__((aligned(4))); - }; - queued_entry_t* next; - - queued_entry_t() { - next = NULL; - } -}; - -static int cmp(queued_entry_t* a, queued_entry_t* b) { - int n = a->entry.sec - b->entry.sec; - if (n != 0) { - return n; - } - return a->entry.nsec - b->entry.nsec; -} - struct log_device_t { - char* device; + const char* device; bool binary; - int fd; + struct logger *logger; + struct logger_list *logger_list; bool printed; char label; - queued_entry_t* queue; log_device_t* next; - log_device_t(char* d, bool b, char l) { + log_device_t(const char* d, bool b, char l) { device = d; binary = b; label = l; - queue = NULL; next = NULL; printed = false; } - - void enqueue(queued_entry_t* entry) { - if (this->queue == NULL) { - this->queue = entry; - } else { - queued_entry_t** e = &this->queue; - while (*e && cmp(entry, *e) >= 0) { - e = &((*e)->next); - } - entry->next = *e; - *e = entry; - } - } }; namespace android { @@ -147,17 +111,14 @@ static void rotateLogs() } -void printBinary(struct logger_entry *buf) +void printBinary(struct log_msg *buf) { - size_t size = sizeof(logger_entry) + buf->len; - int ret; - - do { - ret = write(g_outFD, buf, size); - } while (ret < 0 && errno == EINTR); + size_t size = buf->len(); + + TEMP_FAILURE_RETRY(write(g_outFD, buf, size)); } -static void processBuffer(log_device_t* dev, struct logger_entry *buf) +static void processBuffer(log_device_t* dev, struct log_msg *buf) { int bytesWritten = 0; int err; @@ -165,12 +126,14 @@ static void processBuffer(log_device_t* dev, struct logger_entry *buf) char binaryMsgBuf[1024]; if (dev->binary) { - err = android_log_processBinaryLogBuffer(buf, &entry, g_eventTagMap, - binaryMsgBuf, sizeof(binaryMsgBuf)); + err = android_log_processBinaryLogBuffer(&buf->entry_v1, &entry, + g_eventTagMap, + binaryMsgBuf, + sizeof(binaryMsgBuf)); //printf(">>> pri=%d len=%d msg='%s'\n", // entry.priority, entry.messageLen, entry.message); } else { - err = android_log_processLogBuffer(buf, &entry); + err = android_log_processLogBuffer(&buf->entry_v1, &entry); } if (err < 0) { goto error; @@ -197,7 +160,7 @@ static void processBuffer(log_device_t* dev, struct logger_entry *buf) g_outByteCount += bytesWritten; - if (g_logRotateSizeKBytes > 0 + if (g_logRotateSizeKBytes > 0 && (g_outByteCount / 1024) >= g_logRotateSizeKBytes ) { rotateLogs(); @@ -208,20 +171,13 @@ error: return; } -static void chooseFirst(log_device_t* dev, log_device_t** firstdev) { - for (*firstdev = NULL; dev != NULL; dev = dev->next) { - if (dev->queue != NULL && (*firstdev == NULL || cmp(dev->queue, (*firstdev)->queue) < 0)) { - *firstdev = dev; - } - } -} - static void maybePrintStart(log_device_t* dev) { if (!dev->printed) { dev->printed = true; if (g_devCount > 1 && !g_printBinary) { char buf[1024]; - snprintf(buf, sizeof(buf), "--------- beginning of %s\n", dev->device); + snprintf(buf, sizeof(buf), "--------- beginning of %s\n", + dev->device); if (write(g_outFD, buf, strlen(buf)) < 0) { perror("output error"); exit(-1); @@ -230,145 +186,6 @@ static void maybePrintStart(log_device_t* dev) { } } -static void skipNextEntry(log_device_t* dev) { - maybePrintStart(dev); - queued_entry_t* entry = dev->queue; - dev->queue = entry->next; - delete entry; -} - -static void printNextEntry(log_device_t* dev) { - maybePrintStart(dev); - if (g_printBinary) { - printBinary(&dev->queue->entry); - } else { - processBuffer(dev, &dev->queue->entry); - } - skipNextEntry(dev); -} - -static void readLogLines(log_device_t* devices) -{ - log_device_t* dev; - int max = 0; - int ret; - int queued_lines = 0; - bool sleep = false; - - int result; - fd_set readset; - - for (dev=devices; dev; dev = dev->next) { - if (dev->fd > max) { - max = dev->fd; - } - } - - while (1) { - do { - timeval timeout = { 0, 5000 /* 5ms */ }; // If we oversleep it's ok, i.e. ignore EINTR. - FD_ZERO(&readset); - for (dev=devices; dev; dev = dev->next) { - FD_SET(dev->fd, &readset); - } - result = select(max + 1, &readset, NULL, NULL, sleep ? NULL : &timeout); - } while (result == -1 && errno == EINTR); - - if (result >= 0) { - for (dev=devices; dev; dev = dev->next) { - if (FD_ISSET(dev->fd, &readset)) { - queued_entry_t* entry = new queued_entry_t(); - /* NOTE: driver guarantees we read exactly one full entry */ - ret = read(dev->fd, entry->buf, LOGGER_ENTRY_MAX_LEN); - if (ret < 0) { - if (errno == EINTR) { - delete entry; - goto next; - } - if (errno == EAGAIN) { - delete entry; - break; - } - perror("logcat read"); - exit(EXIT_FAILURE); - } - else if (!ret) { - fprintf(stderr, "read: Unexpected EOF!\n"); - exit(EXIT_FAILURE); - } - else if (entry->entry.len != ret - sizeof(struct logger_entry)) { - fprintf(stderr, "read: unexpected length. Expected %d, got %d\n", - entry->entry.len, ret - sizeof(struct logger_entry)); - exit(EXIT_FAILURE); - } - - entry->entry.msg[entry->entry.len] = '\0'; - - dev->enqueue(entry); - ++queued_lines; - } - } - - if (result == 0) { - // we did our short timeout trick and there's nothing new - // print everything we have and wait for more data - sleep = true; - while (true) { - chooseFirst(devices, &dev); - if (dev == NULL) { - break; - } - if (g_tail_lines == 0 || queued_lines <= g_tail_lines) { - printNextEntry(dev); - } else { - skipNextEntry(dev); - } - --queued_lines; - } - - // the caller requested to just dump the log and exit - if (g_nonblock) { - return; - } - } else { - // print all that aren't the last in their list - sleep = false; - while (g_tail_lines == 0 || queued_lines > g_tail_lines) { - chooseFirst(devices, &dev); - if (dev == NULL || dev->queue->next == NULL) { - break; - } - if (g_tail_lines == 0) { - printNextEntry(dev); - } else { - skipNextEntry(dev); - } - --queued_lines; - } - } - } -next: - ; - } -} - -static int clearLog(int logfd) -{ - return ioctl(logfd, LOGGER_FLUSH_LOG); -} - -/* returns the total size of the log's ring buffer */ -static int getLogSize(int logfd) -{ - return ioctl(logfd, LOGGER_GET_LOG_BUF_SIZE); -} - -/* returns the readable size of the log's ring buffer (that is, amount of the log consumed) */ -static int getLogReadableSize(int logfd) -{ - return ioctl(logfd, LOGGER_GET_LOG_LEN); -} - static void setupOutput() { @@ -406,6 +223,7 @@ static void show_help(const char *cmd) " -c clear (flush) the entire log and exit\n" " -d dump the log and then exit (don't block)\n" " -t <count> print only the most recent <count> lines (implies -d)\n" + " -T <count> print only the most recent <count> lines (does not imply -d)\n" " -g get the size of the log's ring buffer and exit\n" " -b <buffer> Request alternate ring buffer, 'main', 'system', 'radio'\n" " or 'events'. Multiple -b parameters are allowed and the\n" @@ -465,6 +283,10 @@ int main(int argc, char **argv) log_device_t* devices = NULL; log_device_t* dev; bool needBinary = false; + struct logger_list *logger_list; + int tail_lines = 0; + + signal(SIGPIPE, exit); g_logformat = android_log_format_new(); @@ -481,14 +303,14 @@ int main(int argc, char **argv) for (;;) { int ret; - ret = getopt(argc, argv, "cdt:gsQf:r::n:v:b:B"); + ret = getopt(argc, argv, "cdt:T:gsQf:r::n:v:b:B"); if (ret < 0) { break; } switch(ret) { - case 's': + case 's': // default to all silent android_log_addFilterRule(g_logformat, "*:s"); break; @@ -499,12 +321,14 @@ int main(int argc, char **argv) break; case 'd': - g_nonblock = true; + mode = O_RDONLY | O_NDELAY; break; case 't': - g_nonblock = true; - g_tail_lines = atoi(optarg); + mode = O_RDONLY | O_NDELAY; + /* FALLTHRU */ + case 'T': + tail_lines = atoi(optarg); break; case 'g': @@ -512,10 +336,6 @@ int main(int argc, char **argv) break; case 'b': { - char* buf = (char*) malloc(strlen(LOG_FILE_DIR) + strlen(optarg) + 1); - strcpy(buf, LOG_FILE_DIR); - strcat(buf, optarg); - bool binary = strcmp(optarg, "events") == 0; if (binary) { needBinary = true; @@ -526,9 +346,9 @@ int main(int argc, char **argv) while (dev->next) { dev = dev->next; } - dev->next = new log_device_t(buf, binary, optarg[0]); + dev->next = new log_device_t(optarg, binary, optarg[0]); } else { - devices = new log_device_t(buf, binary, optarg[0]); + devices = new log_device_t(optarg, binary, optarg[0]); } android::g_devCount++; } @@ -546,8 +366,8 @@ int main(int argc, char **argv) break; case 'r': - if (optarg == NULL) { - android::g_logRotateSizeKBytes + if (optarg == NULL) { + android::g_logRotateSizeKBytes = DEFAULT_LOG_ROTATE_SIZE_KBYTES; } else { long logRotateSize; @@ -659,19 +479,15 @@ int main(int argc, char **argv) } if (!devices) { - devices = new log_device_t(strdup("/dev/"LOGGER_LOG_MAIN), false, 'm'); + devices = new log_device_t("main", false, 'm'); android::g_devCount = 1; - int accessmode = - (mode & O_RDONLY) ? R_OK : 0 - | (mode & O_WRONLY) ? W_OK : 0; - // only add this if it's available - if (0 == access("/dev/"LOGGER_LOG_SYSTEM, accessmode)) { - devices->next = new log_device_t(strdup("/dev/"LOGGER_LOG_SYSTEM), false, 's'); + if (android_name_to_log_id("system") == LOG_ID_SYSTEM) { + devices->next = new log_device_t("system", false, 's'); android::g_devCount++; } } - if (android::g_logRotateSizeKBytes != 0 + if (android::g_logRotateSizeKBytes != 0 && android::g_outputFileName == NULL ) { fprintf(stderr,"-r requires -f as well\n"); @@ -688,7 +504,7 @@ int main(int argc, char **argv) err = setLogFormat(logFormat); if (err < 0) { - fprintf(stderr, "invalid format in ANDROID_PRINTF_LOG '%s'\n", + fprintf(stderr, "invalid format in ANDROID_PRINTF_LOG '%s'\n", logFormat); } } @@ -707,8 +523,8 @@ int main(int argc, char **argv) if (env_tags_orig != NULL) { err = android_log_addFilterString(g_logformat, env_tags_orig); - if (err < 0) { - fprintf(stderr, "Invalid filter expression in" + if (err < 0) { + fprintf(stderr, "Invalid filter expression in" " ANDROID_LOG_TAGS\n"); android::show_help(argv[0]); exit(-1); @@ -719,7 +535,7 @@ int main(int argc, char **argv) for (int i = optind ; i < argc ; i++) { err = android_log_addFilterString(g_logformat, argv[i]); - if (err < 0) { + if (err < 0) { fprintf (stderr, "Invalid filter expression '%s'\n", argv[i]); android::show_help(argv[0]); exit(-1); @@ -728,19 +544,21 @@ int main(int argc, char **argv) } dev = devices; + logger_list = android_logger_list_alloc(mode, tail_lines, 0); while (dev) { - dev->fd = open(dev->device, mode); - if (dev->fd < 0) { - fprintf(stderr, "Unable to open log device '%s': %s\n", - dev->device, strerror(errno)); + dev->logger_list = logger_list; + dev->logger = android_logger_open(logger_list, + android_name_to_log_id(dev->device)); + if (!dev->logger) { + fprintf(stderr, "Unable to open log device '%s'\n", dev->device); exit(EXIT_FAILURE); } if (clearLog) { int ret; - ret = android::clearLog(dev->fd); + ret = android_logger_clear(dev->logger); if (ret) { - perror("ioctl"); + perror("clearLog"); exit(EXIT_FAILURE); } } @@ -748,15 +566,15 @@ int main(int argc, char **argv) if (getLogSize) { int size, readable; - size = android::getLogSize(dev->fd); + size = android_logger_get_log_size(dev->logger); if (size < 0) { - perror("ioctl"); + perror("getLogSize"); exit(EXIT_FAILURE); } - readable = android::getLogReadableSize(dev->fd); + readable = android_logger_get_log_readable_size(dev->logger); if (readable < 0) { - perror("ioctl"); + perror("getLogReadableSize"); exit(EXIT_FAILURE); } @@ -783,7 +601,51 @@ int main(int argc, char **argv) if (needBinary) android::g_eventTagMap = android_openEventTagMap(EVENT_TAG_MAP_FILE); - android::readLogLines(devices); + while (1) { + struct log_msg log_msg; + int ret = android_logger_list_read(logger_list, &log_msg); + + if (ret == 0) { + fprintf(stderr, "read: Unexpected EOF!\n"); + exit(EXIT_FAILURE); + } + + if (ret < 0) { + if (ret == -EAGAIN) { + break; + } + + if (ret == -EIO) { + fprintf(stderr, "read: Unexpected EOF!\n"); + exit(EXIT_FAILURE); + } + if (ret == -EINVAL) { + fprintf(stderr, "read: unexpected length.\n"); + exit(EXIT_FAILURE); + } + perror("logcat read"); + exit(EXIT_FAILURE); + } + + for(dev = devices; dev; dev = dev->next) { + if (android_name_to_log_id(dev->device) == log_msg.id()) { + break; + } + } + if (!dev) { + fprintf(stderr, "read: Unexpected log ID!\n"); + exit(EXIT_FAILURE); + } + + android::maybePrintStart(dev); + if (android::g_printBinary) { + android::printBinary(&log_msg); + } else { + android::processBuffer(dev, &log_msg); + } + } + + android_logger_list_free(logger_list); return 0; } diff --git a/logcat/tests/Android.mk b/logcat/tests/Android.mk new file mode 100644 index 0000000..bdaec14 --- /dev/null +++ b/logcat/tests/Android.mk @@ -0,0 +1,46 @@ +# +# Copyright (C) 2013-2014 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. +# + +LOCAL_PATH := $(call my-dir) + +# ----------------------------------------------------------------------------- +# Unit tests. +# ----------------------------------------------------------------------------- + +test_module := logcat-unit-tests +test_tags := tests + +test_c_flags := \ + -fstack-protector-all \ + -g \ + -Wall -Wextra \ + -Werror \ + -fno-builtin \ + +test_src_files := \ + logcat_test.cpp \ + +# Build tests for the device (with .so). Run with: +# adb shell /data/nativetest/logcat-unit-tests/logcat-unit-tests +include $(CLEAR_VARS) +LOCAL_MODULE := $(test_module) +LOCAL_MODULE_TAGS := $(test_tags) +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk +LOCAL_CFLAGS += $(test_c_flags) +LOCAL_LDLIBS := -lpthread +LOCAL_SHARED_LIBRARIES := liblog +LOCAL_SRC_FILES := $(test_src_files) +include $(BUILD_NATIVE_TEST) diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp new file mode 100644 index 0000000..f963a3a --- /dev/null +++ b/logcat/tests/logcat_test.cpp @@ -0,0 +1,443 @@ +/* + * Copyright (C) 2013-2014 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 <signal.h> +#include <stdio.h> +#include <gtest/gtest.h> +#include <log/log.h> +#include <log/logger.h> +#include <log/log_read.h> + +// enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and +// non-syscall libs. Since we are only using this in the emergency of +// a signal to stuff a terminating code into the logs, we will spin rather +// than try a usleep. +#define LOG_FAILURE_RETRY(exp) ({ \ + typeof (exp) _rc; \ + do { \ + _rc = (exp); \ + } while (((_rc == -1) \ + && ((errno == EINTR) \ + || (errno == EAGAIN))) \ + || (_rc == -EINTR) \ + || (_rc == -EAGAIN)); \ + _rc; }) + +static const char begin[] = "--------- beginning of "; + +TEST(logcat, sorted_order) { + FILE *fp; + + ASSERT_EQ(0, NULL == (fp = popen( + "logcat -v time -b radio -b events -b system -b main -d 2>/dev/null", + "r"))); + + class timestamp { + private: + int month; + int day; + int hour; + int minute; + int second; + int millisecond; + bool ok; + + public: + void init(const char *buffer) + { + ok = false; + if (buffer != NULL) { + ok = sscanf(buffer, "%d-%d %d:%d:%d.%d ", + &month, &day, &hour, &minute, &second, &millisecond) == 6; + } + } + + timestamp(const char *buffer) + { + init(buffer); + } + + bool operator< (timestamp &T) + { + return !ok || !T.ok + || (month < T.month) + || ((month == T.month) + && ((day < T.day) + || ((day == T.day) + && ((hour < T.hour) + || ((hour == T.hour) + && ((minute < T.minute) + || ((minute == T.minute) + && ((second < T.second) + || ((second == T.second) + && (millisecond < T.millisecond)))))))))); + } + + bool valid(void) + { + return ok; + } + } last(NULL); + + char buffer[5120]; + + int count = 0; + + while (fgets(buffer, sizeof(buffer), fp)) { + if (!strncmp(begin, buffer, sizeof(begin) - 1)) { + continue; + } + if (!last.valid()) { + last.init(buffer); + } + timestamp next(buffer); + ASSERT_EQ(0, next < last); + if (next.valid()) { + last.init(buffer); + } + ++count; + } + + pclose(fp); + + ASSERT_LT(100, count); +} + +TEST(logcat, buckets) { + FILE *fp; + + ASSERT_EQ(0, NULL == (fp = popen( + "logcat -b radio -b events -b system -b main -d 2>/dev/null", + "r"))); + + char buffer[5120]; + + int ids = 0; + int count = 0; + + while (fgets(buffer, sizeof(buffer), fp)) { + if (!strncmp(begin, buffer, sizeof(begin) - 1)) { + while (char *cp = strrchr(buffer, '\n')) { + *cp = '\0'; + } + log_id_t id = android_name_to_log_id(buffer + sizeof(begin) - 1); + ids |= 1 << id; + ++count; + } + } + + pclose(fp); + + ASSERT_EQ(15, ids); + + ASSERT_EQ(4, count); +} + +TEST(logcat, tail_3) { + FILE *fp; + + ASSERT_EQ(0, NULL == (fp = popen( + "logcat -v long -b radio -b events -b system -b main -t 3 2>/dev/null", + "r"))); + + char buffer[5120]; + + int count = 0; + + while (fgets(buffer, sizeof(buffer), fp)) { + if ((buffer[0] == '[') && (buffer[1] == ' ') + && isdigit(buffer[2]) && isdigit(buffer[3]) + && (buffer[4] == '-')) { + ++count; + } + } + + pclose(fp); + + ASSERT_EQ(3, count); +} + +TEST(logcat, tail_10) { + FILE *fp; + + ASSERT_EQ(0, NULL == (fp = popen( + "logcat -v long -b radio -b events -b system -b main -t 10 2>/dev/null", + "r"))); + + char buffer[5120]; + + int count = 0; + + while (fgets(buffer, sizeof(buffer), fp)) { + if ((buffer[0] == '[') && (buffer[1] == ' ') + && isdigit(buffer[2]) && isdigit(buffer[3]) + && (buffer[4] == '-')) { + ++count; + } + } + + pclose(fp); + + ASSERT_EQ(10, count); +} + +TEST(logcat, tail_100) { + FILE *fp; + + ASSERT_EQ(0, NULL == (fp = popen( + "logcat -v long -b radio -b events -b system -b main -t 100 2>/dev/null", + "r"))); + + char buffer[5120]; + + int count = 0; + + while (fgets(buffer, sizeof(buffer), fp)) { + if ((buffer[0] == '[') && (buffer[1] == ' ') + && isdigit(buffer[2]) && isdigit(buffer[3]) + && (buffer[4] == '-')) { + ++count; + } + } + + pclose(fp); + + ASSERT_EQ(100, count); +} + +TEST(logcat, tail_1000) { + FILE *fp; + + ASSERT_EQ(0, NULL == (fp = popen( + "logcat -v long -b radio -b events -b system -b main -t 1000 2>/dev/null", + "r"))); + + char buffer[5120]; + + int count = 0; + + while (fgets(buffer, sizeof(buffer), fp)) { + if ((buffer[0] == '[') && (buffer[1] == ' ') + && isdigit(buffer[2]) && isdigit(buffer[3]) + && (buffer[4] == '-')) { + ++count; + } + } + + pclose(fp); + + ASSERT_EQ(1000, count); +} + +TEST(logcat, End_to_End) { + pid_t pid = getpid(); + + log_time ts(CLOCK_MONOTONIC); + + ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts))); + + FILE *fp; + ASSERT_EQ(0, NULL == (fp = popen( + "logcat -b events -t 100 2>/dev/null", + "r"))); + + char buffer[5120]; + + int count = 0; + + while (fgets(buffer, sizeof(buffer), fp)) { + int p; + unsigned long long t; + + if ((2 != sscanf(buffer, "I/[0] ( %d): %llu", &p, &t)) + || (p != pid)) { + continue; + } + + log_time tx((const char *) &t); + if (ts == tx) { + ++count; + } + } + + pclose(fp); + + ASSERT_EQ(1, count); +} + +TEST(logcat, get_) { + FILE *fp; + + ASSERT_EQ(0, NULL == (fp = popen( + "logcat -b radio -b events -b system -b main -g 2>/dev/null", + "r"))); + + char buffer[5120]; + + int count = 0; + + while (fgets(buffer, sizeof(buffer), fp)) { + int size, consumed, max, payload; + + size = consumed = max = payload = 0; + if ((4 == sscanf(buffer, "%*s ring buffer is %dKb (%dKb consumed)," + " max entry is %db, max payload is %db", + &size, &consumed, &max, &payload)) + && ((size * 3) >= consumed) + && ((size * 1024) > max) + && (max > payload)) { + ++count; + } + } + + pclose(fp); + + ASSERT_EQ(4, count); +} + +static void caught_blocking(int signum) +{ + unsigned long long v = 0xDEADBEEFA55A0000ULL; + + v += getpid() & 0xFFFF; + + LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v))); +} + +TEST(logcat, blocking) { + FILE *fp; + unsigned long long v = 0xDEADBEEFA55A0000ULL; + + pid_t pid = getpid(); + + v += pid & 0xFFFF; + + ASSERT_EQ(0, NULL == (fp = popen( + "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&" + " logcat -b events 2>&1", + "r"))); + + char buffer[5120]; + + int count = 0; + + int signals = 0; + + signal(SIGALRM, caught_blocking); + alarm(2); + while (fgets(buffer, sizeof(buffer), fp)) { + alarm(2); + + ++count; + + if (!strncmp(buffer, "DONE", 4)) { + break; + } + + int p; + unsigned long long l; + + if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l)) + || (p != pid)) { + continue; + } + + if (l == v) { + ++signals; + break; + } + } + alarm(0); + signal(SIGALRM, SIG_DFL); + + // Generate SIGPIPE + fclose(fp); + caught_blocking(0); + + pclose(fp); + + ASSERT_LT(10, count); + + ASSERT_EQ(1, signals); +} + +static void caught_blocking_tail(int signum) +{ + unsigned long long v = 0xA55ADEADBEEF0000ULL; + + v += getpid() & 0xFFFF; + + LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v))); +} + +TEST(logcat, blocking_tail) { + FILE *fp; + unsigned long long v = 0xA55ADEADBEEF0000ULL; + + pid_t pid = getpid(); + + v += pid & 0xFFFF; + + ASSERT_EQ(0, NULL == (fp = popen( + "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&" + " logcat -b events -T 5 2>&1", + "r"))); + + char buffer[5120]; + + int count = 0; + + int signals = 0; + + signal(SIGALRM, caught_blocking_tail); + alarm(2); + while (fgets(buffer, sizeof(buffer), fp)) { + alarm(2); + + ++count; + + if (!strncmp(buffer, "DONE", 4)) { + break; + } + + int p; + unsigned long long l; + + if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l)) + || (p != pid)) { + continue; + } + + if (l == v) { + if (count >= 5) { + ++signals; + } + break; + } + } + alarm(0); + signal(SIGALRM, SIG_DFL); + + /* Generate SIGPIPE */ + fclose(fp); + caught_blocking_tail(0); + + pclose(fp); + + ASSERT_LT(5, count); + + ASSERT_EQ(1, signals); +} diff --git a/logwrapper/logwrapper.c b/logwrapper/logwrapper.c index d0d8d14..9e0385d 100644 --- a/logwrapper/logwrapper.c +++ b/logwrapper/logwrapper.c @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <errno.h> #include <stdio.h> #include <stdlib.h> #include <sys/wait.h> @@ -89,7 +90,8 @@ int main(int argc, char* argv[]) { } if (seg_fault_on_exit) { - *(int *)status = 0; // causes SIGSEGV with fault_address = status + uintptr_t fault_address = (uintptr_t) status; + *(int *) fault_address = 0; // causes SIGSEGV with fault_address = status } return rc; diff --git a/mkbootimg/bootimg.h b/mkbootimg/bootimg.h index 242ab35..9171d85 100644 --- a/mkbootimg/bootimg.h +++ b/mkbootimg/bootimg.h @@ -24,6 +24,7 @@ typedef struct boot_img_hdr boot_img_hdr; #define BOOT_MAGIC_SIZE 8 #define BOOT_NAME_SIZE 16 #define BOOT_ARGS_SIZE 512 +#define BOOT_EXTRA_ARGS_SIZE 1024 struct boot_img_hdr { @@ -43,10 +44,14 @@ struct boot_img_hdr unsigned unused[2]; /* future expansion: should be 0 */ unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */ - + unsigned char cmdline[BOOT_ARGS_SIZE]; unsigned id[8]; /* timestamp / checksum / sha1 / etc */ + + /* Supplemental command line data; kept here to maintain + * binary compatibility with older versions of mkbootimg */ + unsigned char extra_cmdline[BOOT_EXTRA_ARGS_SIZE]; }; /* diff --git a/mkbootimg/mkbootimg.c b/mkbootimg/mkbootimg.c index 34a879b..d598f03 100644 --- a/mkbootimg/mkbootimg.c +++ b/mkbootimg/mkbootimg.c @@ -114,6 +114,7 @@ int main(int argc, char **argv) unsigned ramdisk_offset = 0x01000000; unsigned second_offset = 0x00f00000; unsigned tags_offset = 0x00000100; + size_t cmdlen; argc--; argv++; @@ -192,11 +193,19 @@ int main(int argc, char **argv) memcpy(hdr.magic, BOOT_MAGIC, BOOT_MAGIC_SIZE); - if(strlen(cmdline) > (BOOT_ARGS_SIZE - 1)) { + cmdlen = strlen(cmdline); + if(cmdlen > (BOOT_ARGS_SIZE + BOOT_EXTRA_ARGS_SIZE - 2)) { fprintf(stderr,"error: kernel commandline too large\n"); return 1; } - strcpy((char*)hdr.cmdline, cmdline); + /* Even if we need to use the supplemental field, ensure we + * are still NULL-terminated */ + strncpy((char *)hdr.cmdline, cmdline, BOOT_ARGS_SIZE - 1); + hdr.cmdline[BOOT_ARGS_SIZE - 1] = '\0'; + if (cmdlen >= (BOOT_ARGS_SIZE - 1)) { + cmdline += (BOOT_ARGS_SIZE - 1); + strncpy((char *)hdr.extra_cmdline, cmdline, BOOT_EXTRA_ARGS_SIZE); + } kernel_data = load_file(kernel_fn, &hdr.kernel_size); if(kernel_data == 0) { diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in index d2f74c0..927c33d 100644 --- a/rootdir/init.environ.rc.in +++ b/rootdir/init.environ.rc.in @@ -1,7 +1,6 @@ # set up the global environment on init export PATH /sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin - export LD_LIBRARY_PATH /vendor/lib:/system/lib export ANDROID_BOOTLOGO 1 export ANDROID_ROOT /system export ANDROID_ASSETS /system/app diff --git a/rootdir/init.rc b/rootdir/init.rc index 1ee9fbf..90c8187 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -13,10 +13,16 @@ on early-init # Set init and its forked children's oom_adj. write /proc/1/oom_adj -16 + # Apply strict SELinux checking of PROT_EXEC on mmap/mprotect calls. + write /sys/fs/selinux/checkreqprot 0 + # Set the security context for the init process. # This should occur before anything else (e.g. ueventd) is started. setcon u:r:init:s0 + # Set the security context of /adb_keys if present. + restorecon /adb_keys + start ueventd # create mountpoints @@ -82,6 +88,10 @@ loglevel 3 mkdir /mnt/obb 0700 root system mount tmpfs tmpfs /mnt/obb mode=0755,gid=1000 + # memory control cgroup + mkdir /dev/memcg 0700 root system + mount cgroup none /dev/memcg memory + write /proc/sys/kernel/panic_on_oops 1 write /proc/sys/kernel/hung_task_timeout_secs 0 write /proc/cpu/alignment 4 @@ -213,13 +223,23 @@ on post-fs-data mkdir /data/misc/radio 0770 system radio mkdir /data/misc/sms 0770 system radio mkdir /data/misc/zoneinfo 0775 system system + restorecon_recursive /data/misc/zoneinfo 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 mkdir /data/misc/wifi 0770 wifi wifi + mkdir /data/misc/wifi/sockets 0770 wifi wifi + restorecon_recursive /data/misc/wifi/sockets + mkdir /data/misc/wifi/wpa_supplicant 0770 wifi wifi + mkdir /data/misc/dhcp 0770 dhcp dhcp + # give system access to wpa_supplicant.conf for backup and restore chmod 0660 /data/misc/wifi/wpa_supplicant.conf mkdir /data/local 0751 root root mkdir /data/misc/media 0700 media media + restorecon_recursive /data/misc/media + + # Set security context of any pre-existing /data/misc/adb/adb_keys file. + restorecon /data/misc/adb + restorecon /data/misc/adb/adb_keys # For security reasons, /data/local/tmp should always be empty. # Do not place files or directories in /data/local/tmp @@ -251,6 +271,7 @@ on post-fs-data # create directory for MediaDrm plug-ins - give drm the read/write access to # the following directory. mkdir /data/mediadrm 0770 mediadrm mediadrm + restorecon_recursive /data/mediadrm # symlink to bugreport storage location symlink /data/data/com.android.shell/files/bugreports /data/bugreports @@ -258,6 +279,9 @@ on post-fs-data # Separate location for storing security policy files on data mkdir /data/security 0711 system system + # Reload policy from /data/security if present. + setprop selinux.reload_policy 1 + # 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. @@ -351,23 +375,20 @@ on boot chown system system /sys/kernel/ipv4/tcp_rmem_max chown root radio /proc/cmdline -# Set these so we can remotely update SELinux policy - chown system system /sys/fs/selinux/load - chown system system /sys/fs/selinux/enforce - # Define TCP buffer sizes for various networks # ReadMin, ReadInitial, ReadMax, WriteMin, WriteInitial, WriteMax, - setprop net.tcp.buffersize.default 4096,87380,110208,4096,16384,110208 - setprop net.tcp.buffersize.wifi 524288,1048576,2097152,262144,524288,1048576 - setprop net.tcp.buffersize.lte 524288,1048576,2097152,262144,524288,1048576 - setprop net.tcp.buffersize.umts 4094,87380,110208,4096,16384,110208 - setprop net.tcp.buffersize.hspa 4094,87380,262144,4096,16384,262144 - setprop net.tcp.buffersize.hsupa 4094,87380,262144,4096,16384,262144 - setprop net.tcp.buffersize.hsdpa 4094,87380,262144,4096,16384,262144 - setprop net.tcp.buffersize.hspap 4094,87380,1220608,4096,16384,1220608 - setprop net.tcp.buffersize.edge 4093,26280,35040,4096,16384,35040 - setprop net.tcp.buffersize.gprs 4092,8760,11680,4096,8760,11680 - setprop net.tcp.buffersize.evdo 4094,87380,262144,4096,16384,262144 + setprop net.tcp.buffersize.default 4096,87380,110208,4096,16384,110208 + setprop net.tcp.buffersize.wifi 524288,1048576,2097152,262144,524288,1048576 + setprop net.tcp.buffersize.ethernet 524288,1048576,3145728,524288,1048576,2097152 + setprop net.tcp.buffersize.lte 524288,1048576,2097152,262144,524288,1048576 + setprop net.tcp.buffersize.umts 4094,87380,110208,4096,16384,110208 + setprop net.tcp.buffersize.hspa 4094,87380,262144,4096,16384,262144 + setprop net.tcp.buffersize.hsupa 4094,87380,262144,4096,16384,262144 + setprop net.tcp.buffersize.hsdpa 4094,87380,262144,4096,16384,262144 + setprop net.tcp.buffersize.hspap 4094,87380,1220608,4096,16384,1220608 + setprop net.tcp.buffersize.edge 4093,26280,35040,4096,16384,35040 + setprop net.tcp.buffersize.gprs 4092,8760,11680,4096,8760,11680 + setprop net.tcp.buffersize.evdo 4094,87380,262144,4096,16384,262144 # Define default initial receive window size in segments. setprop net.tcp.default_init_rwnd 60 @@ -431,22 +452,19 @@ service healthd-charger /sbin/healthd -n critical seclabel u:r:healthd:s0 -on property:selinux.reload_policy=1 - restart ueventd - restart installd - service console /system/bin/sh class core console disabled user shell group log + seclabel u:r:shell:s0 on property:ro.debuggable=1 start console # adbd is controlled via property triggers in init.<platform>.usb.rc -service adbd /sbin/adbd +service adbd /sbin/adbd --root_seclabel=u:r:su:s0 class core socket adbd stream 660 system system disabled @@ -456,6 +474,11 @@ service adbd /sbin/adbd on property:ro.kernel.qemu=1 start adbd +service lmkd /system/bin/lmkd + class core + critical + socket lmkd seqpacket 0660 system system + service servicemanager /system/bin/servicemanager class core user system diff --git a/rootdir/init.trace.rc b/rootdir/init.trace.rc index 8a05fd0..50944e6 100644 --- a/rootdir/init.trace.rc +++ b/rootdir/init.trace.rc @@ -9,6 +9,7 @@ on boot chown root shell /sys/kernel/debug/tracing/trace_clock chown root shell /sys/kernel/debug/tracing/buffer_size_kb chown root shell /sys/kernel/debug/tracing/options/overwrite + chown root shell /sys/kernel/debug/tracing/options/print-tgid chown root shell /sys/kernel/debug/tracing/events/sched/sched_switch/enable chown root shell /sys/kernel/debug/tracing/events/sched/sched_wakeup/enable chown root shell /sys/kernel/debug/tracing/events/power/cpu_frequency/enable @@ -20,6 +21,7 @@ on boot chmod 0664 /sys/kernel/debug/tracing/trace_clock chmod 0664 /sys/kernel/debug/tracing/buffer_size_kb chmod 0664 /sys/kernel/debug/tracing/options/overwrite + chmod 0664 /sys/kernel/debug/tracing/options/print-tgid chmod 0664 /sys/kernel/debug/tracing/events/sched/sched_switch/enable chmod 0664 /sys/kernel/debug/tracing/events/sched/sched_wakeup/enable chmod 0664 /sys/kernel/debug/tracing/events/power/cpu_frequency/enable diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc index 2cf0265..b8fe716 100644 --- a/rootdir/ueventd.rc +++ b/rootdir/ueventd.rc @@ -1,3 +1,6 @@ +subsystem adf + devname uevent_devname + /dev/null 0666 root root /dev/zero 0666 root root /dev/full 0666 root root @@ -5,6 +8,8 @@ /dev/tty 0666 root root /dev/random 0666 root root /dev/urandom 0666 root root +# Make HW RNG readable by group system to let EntropyMixer read it. +/dev/hw_random 0440 root system /dev/ashmem 0666 root root /dev/binder 0666 root root @@ -30,6 +35,7 @@ /dev/uhid 0660 system net_bt_stack /dev/uinput 0660 system net_bt_stack /dev/alarm 0664 system radio +/dev/rtc0 0640 system system /dev/tty0 0660 root system /dev/graphics/* 0660 root graphics /dev/msm_hw3dm 0660 system graphics diff --git a/sh/Android.mk b/sh/Android.mk deleted file mode 100644 index dcd13d8..0000000 --- a/sh/Android.mk +++ /dev/null @@ -1,70 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ - alias.c \ - arith.c \ - arith_lex.c \ - builtins.c \ - cd.c \ - error.c \ - eval.c \ - exec.c \ - expand.c \ - input.c \ - jobs.c \ - main.c \ - memalloc.c \ - miscbltin.c \ - mystring.c \ - nodes.c \ - options.c \ - parser.c \ - redir.c \ - show.c \ - syntax.c \ - trap.c \ - output.c \ - var.c \ - bltin/echo.c \ - init.c - -LOCAL_MODULE:= ash -LOCAL_MODULE_TAGS:= shell_ash - -LOCAL_CFLAGS += -DSHELL -DWITH_LINENOISE - -LOCAL_STATIC_LIBRARIES := liblinenoise - -LOCAL_C_INCLUDES += system/core/liblinenoise - -make_ash_files: PRIVATE_SRC_FILES := $(SRC_FILES) -make_ash_files: PRIVATE_CFLAGS := $(LOCAL_CFLAGS) -make_ash_files: - p4 edit arith.c arith_lex.c arith.h builtins.h builtins.c - p4 edit init.c nodes.c nodes.h token.h - sh ./mktokens - bison -o arith.c arith.y - flex -o arith_lex.c arith_lex.l - perl -ne 'print if ( /^\#\s*define\s+ARITH/ );' < arith.c > arith.h - sh ./mkbuiltins shell.h builtins.def . -Wall -O2 - sh ./mknodes.sh nodetypes nodes.c.pat . - sh ./mkinit.sh $(PRIVATE_SRC_FILES) - -include $(BUILD_EXECUTABLE) - - -# create /system/bin/sh symlink to $(TARGET_SHELL) -# not the optimal place for this, but a fitting one - -OUTSYSTEMBINSH := $(TARGET_OUT)/bin/sh -LOCAL_MODULE := systembinsh -$(OUTSYSTEMBINSH): | $(TARGET_SHELL) -$(OUTSYSTEMBINSH): LOCAL_MODULE := $(LOCAL_MODULE) -$(OUTSYSTEMBINSH): - @echo "Symlink: $@ -> $(TARGET_SHELL)" - @rm -rf $@ - $(hide) ln -sf $(TARGET_SHELL) $@ - -ALL_DEFAULT_INSTALLED_MODULES += $(OUTSYSTEMBINSH) -ALL_MODULES.$(LOCAL_MODULE).INSTALLED += $(OUTSYSTEMBINSH) diff --git a/sh/MODULE_LICENSE_BSD b/sh/MODULE_LICENSE_BSD deleted file mode 100644 index e69de29..0000000 --- a/sh/MODULE_LICENSE_BSD +++ /dev/null diff --git a/sh/NOTICE b/sh/NOTICE deleted file mode 100644 index 49a66d2..0000000 --- a/sh/NOTICE +++ /dev/null @@ -1,31 +0,0 @@ -Copyright (c) 1991, 1993 - The Regents of the University of California. All rights reserved. - -This code is derived from software contributed to Berkeley by -Kenneth Almquist. - -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. - - diff --git a/sh/TOUR b/sh/TOUR deleted file mode 100644 index f5c00c4..0000000 --- a/sh/TOUR +++ /dev/null @@ -1,357 +0,0 @@ -# $NetBSD: TOUR,v 1.8 1996/10/16 14:24:56 christos Exp $ -# @(#)TOUR 8.1 (Berkeley) 5/31/93 - -NOTE -- This is the original TOUR paper distributed with ash and -does not represent the current state of the shell. It is provided anyway -since it provides helpful information for how the shell is structured, -but be warned that things have changed -- the current shell is -still under development. - -================================================================ - - A Tour through Ash - - Copyright 1989 by Kenneth Almquist. - - -DIRECTORIES: The subdirectory bltin contains commands which can -be compiled stand-alone. The rest of the source is in the main -ash directory. - -SOURCE CODE GENERATORS: Files whose names begin with "mk" are -programs that generate source code. A complete list of these -programs is: - - program intput files generates - ------- ------------ --------- - mkbuiltins builtins builtins.h builtins.c - mkinit *.c init.c - mknodes nodetypes nodes.h nodes.c - mksignames - signames.h signames.c - mksyntax - syntax.h syntax.c - mktokens - token.h - bltin/mkexpr unary_op binary_op operators.h operators.c - -There are undoubtedly too many of these. Mkinit searches all the -C source files for entries looking like: - - INIT { - x = 1; /* executed during initialization */ - } - - RESET { - x = 2; /* executed when the shell does a longjmp - back to the main command loop */ - } - - SHELLPROC { - x = 3; /* executed when the shell runs a shell procedure */ - } - -It pulls this code out into routines which are when particular -events occur. The intent is to improve modularity by isolating -the information about which modules need to be explicitly -initialized/reset within the modules themselves. - -Mkinit recognizes several constructs for placing declarations in -the init.c file. - INCLUDE "file.h" -includes a file. The storage class MKINIT makes a declaration -available in the init.c file, for example: - MKINIT int funcnest; /* depth of function calls */ -MKINIT alone on a line introduces a structure or union declara- -tion: - MKINIT - struct redirtab { - short renamed[10]; - }; -Preprocessor #define statements are copied to init.c without any -special action to request this. - -INDENTATION: The ash source is indented in multiples of six -spaces. The only study that I have heard of on the subject con- -cluded that the optimal amount to indent is in the range of four -to six spaces. I use six spaces since it is not too big a jump -from the widely used eight spaces. If you really hate six space -indentation, use the adjind (source included) program to change -it to something else. - -EXCEPTIONS: Code for dealing with exceptions appears in -exceptions.c. The C language doesn't include exception handling, -so I implement it using setjmp and longjmp. The global variable -exception contains the type of exception. EXERROR is raised by -calling error. EXINT is an interrupt. EXSHELLPROC is an excep- -tion which is raised when a shell procedure is invoked. The pur- -pose of EXSHELLPROC is to perform the cleanup actions associated -with other exceptions. After these cleanup actions, the shell -can interpret a shell procedure itself without exec'ing a new -copy of the shell. - -INTERRUPTS: In an interactive shell, an interrupt will cause an -EXINT exception to return to the main command loop. (Exception: -EXINT is not raised if the user traps interrupts using the trap -command.) The INTOFF and INTON macros (defined in exception.h) -provide uninterruptable critical sections. Between the execution -of INTOFF and the execution of INTON, interrupt signals will be -held for later delivery. INTOFF and INTON can be nested. - -MEMALLOC.C: Memalloc.c defines versions of malloc and realloc -which call error when there is no memory left. It also defines a -stack oriented memory allocation scheme. Allocating off a stack -is probably more efficient than allocation using malloc, but the -big advantage is that when an exception occurs all we have to do -to free up the memory in use at the time of the exception is to -restore the stack pointer. The stack is implemented using a -linked list of blocks. - -STPUTC: If the stack were contiguous, it would be easy to store -strings on the stack without knowing in advance how long the -string was going to be: - p = stackptr; - *p++ = c; /* repeated as many times as needed */ - stackptr = p; -The folloing three macros (defined in memalloc.h) perform these -operations, but grow the stack if you run off the end: - STARTSTACKSTR(p); - STPUTC(c, p); /* repeated as many times as needed */ - grabstackstr(p); - -We now start a top-down look at the code: - -MAIN.C: The main routine performs some initialization, executes -the user's profile if necessary, and calls cmdloop. Cmdloop is -repeatedly parses and executes commands. - -OPTIONS.C: This file contains the option processing code. It is -called from main to parse the shell arguments when the shell is -invoked, and it also contains the set builtin. The -i and -j op- -tions (the latter turns on job control) require changes in signal -handling. The routines setjobctl (in jobs.c) and setinteractive -(in trap.c) are called to handle changes to these options. - -PARSING: The parser code is all in parser.c. A recursive des- -cent parser is used. Syntax tables (generated by mksyntax) are -used to classify characters during lexical analysis. There are -three tables: one for normal use, one for use when inside single -quotes, and one for use when inside double quotes. The tables -are machine dependent because they are indexed by character vari- -ables and the range of a char varies from machine to machine. - -PARSE OUTPUT: The output of the parser consists of a tree of -nodes. The various types of nodes are defined in the file node- -types. - -Nodes of type NARG are used to represent both words and the con- -tents of here documents. An early version of ash kept the con- -tents of here documents in temporary files, but keeping here do- -cuments in memory typically results in significantly better per- -formance. It would have been nice to make it an option to use -temporary files for here documents, for the benefit of small -machines, but the code to keep track of when to delete the tem- -porary files was complex and I never fixed all the bugs in it. -(AT&T has been maintaining the Bourne shell for more than ten -years, and to the best of my knowledge they still haven't gotten -it to handle temporary files correctly in obscure cases.) - -The text field of a NARG structure points to the text of the -word. The text consists of ordinary characters and a number of -special codes defined in parser.h. The special codes are: - - CTLVAR Variable substitution - CTLENDVAR End of variable substitution - CTLBACKQ Command substitution - CTLBACKQ|CTLQUOTE Command substitution inside double quotes - CTLESC Escape next character - -A variable substitution contains the following elements: - - CTLVAR type name '=' [ alternative-text CTLENDVAR ] - -The type field is a single character specifying the type of sub- -stitution. The possible types are: - - VSNORMAL $var - VSMINUS ${var-text} - VSMINUS|VSNUL ${var:-text} - VSPLUS ${var+text} - VSPLUS|VSNUL ${var:+text} - VSQUESTION ${var?text} - VSQUESTION|VSNUL ${var:?text} - VSASSIGN ${var=text} - VSASSIGN|VSNUL ${var=text} - -In addition, the type field will have the VSQUOTE flag set if the -variable is enclosed in double quotes. The name of the variable -comes next, terminated by an equals sign. If the type is not -VSNORMAL, then the text field in the substitution follows, ter- -minated by a CTLENDVAR byte. - -Commands in back quotes are parsed and stored in a linked list. -The locations of these commands in the string are indicated by -CTLBACKQ and CTLBACKQ+CTLQUOTE characters, depending upon whether -the back quotes were enclosed in double quotes. - -The character CTLESC escapes the next character, so that in case -any of the CTL characters mentioned above appear in the input, -they can be passed through transparently. CTLESC is also used to -escape '*', '?', '[', and '!' characters which were quoted by the -user and thus should not be used for file name generation. - -CTLESC characters have proved to be particularly tricky to get -right. In the case of here documents which are not subject to -variable and command substitution, the parser doesn't insert any -CTLESC characters to begin with (so the contents of the text -field can be written without any processing). Other here docu- -ments, and words which are not subject to splitting and file name -generation, have the CTLESC characters removed during the vari- -able and command substitution phase. Words which are subject -splitting and file name generation have the CTLESC characters re- -moved as part of the file name phase. - -EXECUTION: Command execution is handled by the following files: - eval.c The top level routines. - redir.c Code to handle redirection of input and output. - jobs.c Code to handle forking, waiting, and job control. - exec.c Code to to path searches and the actual exec sys call. - expand.c Code to evaluate arguments. - var.c Maintains the variable symbol table. Called from expand.c. - -EVAL.C: Evaltree recursively executes a parse tree. The exit -status is returned in the global variable exitstatus. The alter- -native entry evalbackcmd is called to evaluate commands in back -quotes. It saves the result in memory if the command is a buil- -tin; otherwise it forks off a child to execute the command and -connects the standard output of the child to a pipe. - -JOBS.C: To create a process, you call makejob to return a job -structure, and then call forkshell (passing the job structure as -an argument) to create the process. Waitforjob waits for a job -to complete. These routines take care of process groups if job -control is defined. - -REDIR.C: Ash allows file descriptors to be redirected and then -restored without forking off a child process. This is accom- -plished by duplicating the original file descriptors. The redir- -tab structure records where the file descriptors have be dupli- -cated to. - -EXEC.C: The routine find_command locates a command, and enters -the command in the hash table if it is not already there. The -third argument specifies whether it is to print an error message -if the command is not found. (When a pipeline is set up, -find_command is called for all the commands in the pipeline be- -fore any forking is done, so to get the commands into the hash -table of the parent process. But to make command hashing as -transparent as possible, we silently ignore errors at that point -and only print error messages if the command cannot be found -later.) - -The routine shellexec is the interface to the exec system call. - -EXPAND.C: Arguments are processed in three passes. The first -(performed by the routine argstr) performs variable and command -substitution. The second (ifsbreakup) performs word splitting -and the third (expandmeta) performs file name generation. If the -"/u" directory is simulated, then when "/u/username" is replaced -by the user's home directory, the flag "didudir" is set. This -tells the cd command that it should print out the directory name, -just as it would if the "/u" directory were implemented using -symbolic links. - -VAR.C: Variables are stored in a hash table. Probably we should -switch to extensible hashing. The variable name is stored in the -same string as the value (using the format "name=value") so that -no string copying is needed to create the environment of a com- -mand. Variables which the shell references internally are preal- -located so that the shell can reference the values of these vari- -ables without doing a lookup. - -When a program is run, the code in eval.c sticks any environment -variables which precede the command (as in "PATH=xxx command") in -the variable table as the simplest way to strip duplicates, and -then calls "environment" to get the value of the environment. -There are two consequences of this. First, if an assignment to -PATH precedes the command, the value of PATH before the assign- -ment must be remembered and passed to shellexec. Second, if the -program turns out to be a shell procedure, the strings from the -environment variables which preceded the command must be pulled -out of the table and replaced with strings obtained from malloc, -since the former will automatically be freed when the stack (see -the entry on memalloc.c) is emptied. - -BUILTIN COMMANDS: The procedures for handling these are scat- -tered throughout the code, depending on which location appears -most appropriate. They can be recognized because their names al- -ways end in "cmd". The mapping from names to procedures is -specified in the file builtins, which is processed by the mkbuil- -tins command. - -A builtin command is invoked with argc and argv set up like a -normal program. A builtin command is allowed to overwrite its -arguments. Builtin routines can call nextopt to do option pars- -ing. This is kind of like getopt, but you don't pass argc and -argv to it. Builtin routines can also call error. This routine -normally terminates the shell (or returns to the main command -loop if the shell is interactive), but when called from a builtin -command it causes the builtin command to terminate with an exit -status of 2. - -The directory bltins contains commands which can be compiled in- -dependently but can also be built into the shell for efficiency -reasons. The makefile in this directory compiles these programs -in the normal fashion (so that they can be run regardless of -whether the invoker is ash), but also creates a library named -bltinlib.a which can be linked with ash. The header file bltin.h -takes care of most of the differences between the ash and the -stand-alone environment. The user should call the main routine -"main", and #define main to be the name of the routine to use -when the program is linked into ash. This #define should appear -before bltin.h is included; bltin.h will #undef main if the pro- -gram is to be compiled stand-alone. - -CD.C: This file defines the cd and pwd builtins. The pwd com- -mand runs /bin/pwd the first time it is invoked (unless the user -has already done a cd to an absolute pathname), but then -remembers the current directory and updates it when the cd com- -mand is run, so subsequent pwd commands run very fast. The main -complication in the cd command is in the docd command, which -resolves symbolic links into actual names and informs the user -where the user ended up if he crossed a symbolic link. - -SIGNALS: Trap.c implements the trap command. The routine set- -signal figures out what action should be taken when a signal is -received and invokes the signal system call to set the signal ac- -tion appropriately. When a signal that a user has set a trap for -is caught, the routine "onsig" sets a flag. The routine dotrap -is called at appropriate points to actually handle the signal. -When an interrupt is caught and no trap has been set for that -signal, the routine "onint" in error.c is called. - -OUTPUT: Ash uses it's own output routines. There are three out- -put structures allocated. "Output" represents the standard out- -put, "errout" the standard error, and "memout" contains output -which is to be stored in memory. This last is used when a buil- -tin command appears in backquotes, to allow its output to be col- -lected without doing any I/O through the UNIX operating system. -The variables out1 and out2 normally point to output and errout, -respectively, but they are set to point to memout when appropri- -ate inside backquotes. - -INPUT: The basic input routine is pgetc, which reads from the -current input file. There is a stack of input files; the current -input file is the top file on this stack. The code allows the -input to come from a string rather than a file. (This is for the --c option and the "." and eval builtin commands.) The global -variable plinno is saved and restored when files are pushed and -popped from the stack. The parser routines store the number of -the current line in this variable. - -DEBUGGING: If DEBUG is defined in shell.h, then the shell will -write debugging information to the file $HOME/trace. Most of -this is done using the TRACE macro, which takes a set of printf -arguments inside two sets of parenthesis. Example: -"TRACE(("n=%d0, n))". The double parenthesis are necessary be- -cause the preprocessor can't handle functions with a variable -number of arguments. Defining DEBUG also causes the shell to -generate a core dump if it is sent a quit signal. The tracing -code is in show.c. diff --git a/sh/ThirdPartyProject.prop b/sh/ThirdPartyProject.prop deleted file mode 100644 index eb9167e..0000000 --- a/sh/ThirdPartyProject.prop +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright 2010 Google Inc. All Rights Reserved. -#Fri Jul 16 10:03:08 PDT 2010 -currentVersion=Unknown -version=1.17 -isNative=true -name=ash -keywords=ash -onDevice=true -homepage=http\://www.in-ulm.de/~mascheck/various/ash/ diff --git a/sh/alias.c b/sh/alias.c deleted file mode 100644 index 59a3dc1..0000000 --- a/sh/alias.c +++ /dev/null @@ -1,273 +0,0 @@ -/* $NetBSD: alias.c,v 1.12 2003/08/07 09:05:29 agc Exp $ */ - -/*- - * Copyright (c) 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * 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[] = "@(#)alias.c 8.3 (Berkeley) 5/4/95"; -#else -__RCSID("$NetBSD: alias.c,v 1.12 2003/08/07 09:05:29 agc Exp $"); -#endif -#endif /* not lint */ - -#include <stdlib.h> -#include "shell.h" -#include "input.h" -#include "output.h" -#include "error.h" -#include "memalloc.h" -#include "mystring.h" -#include "alias.h" -#include "options.h" /* XXX for argptr (should remove?) */ -#include "var.h" - -#define ATABSIZE 39 - -struct alias *atab[ATABSIZE]; - -STATIC void setalias(char *, char *); -STATIC int unalias(char *); -STATIC struct alias **hashalias(char *); - -STATIC -void -setalias(char *name, char *val) -{ - struct alias *ap, **app; - - app = hashalias(name); - for (ap = *app; ap; ap = ap->next) { - if (equal(name, ap->name)) { - INTOFF; - ckfree(ap->val); - ap->val = savestr(val); - INTON; - return; - } - } - /* not found */ - INTOFF; - ap = ckmalloc(sizeof (struct alias)); - ap->name = savestr(name); - /* - * XXX - HACK: in order that the parser will not finish reading the - * alias value off the input before processing the next alias, we - * dummy up an extra space at the end of the alias. This is a crock - * and should be re-thought. The idea (if you feel inclined to help) - * is to avoid alias recursions. The mechanism used is: when - * expanding an alias, the value of the alias is pushed back on the - * input as a string and a pointer to the alias is stored with the - * string. The alias is marked as being in use. When the input - * routine finishes reading the string, it markes the alias not - * in use. The problem is synchronization with the parser. Since - * it reads ahead, the alias is marked not in use before the - * resulting token(s) is next checked for further alias sub. The - * H A C K is that we add a little fluff after the alias value - * so that the string will not be exhausted. This is a good - * idea ------- ***NOT*** - */ -#ifdef notyet - ap->val = savestr(val); -#else /* hack */ - { - int len = strlen(val); - ap->val = ckmalloc(len + 2); - memcpy(ap->val, val, len); - ap->val[len] = ' '; /* fluff */ - ap->val[len+1] = '\0'; - } -#endif - ap->next = *app; - *app = ap; - INTON; -} - -STATIC int -unalias(char *name) -{ - struct alias *ap, **app; - - app = hashalias(name); - - for (ap = *app; ap; app = &(ap->next), ap = ap->next) { - if (equal(name, ap->name)) { - /* - * if the alias is currently in use (i.e. its - * buffer is being used by the input routine) we - * just null out the name instead of freeing it. - * We could clear it out later, but this situation - * is so rare that it hardly seems worth it. - */ - if (ap->flag & ALIASINUSE) - *ap->name = '\0'; - else { - INTOFF; - *app = ap->next; - ckfree(ap->name); - ckfree(ap->val); - ckfree(ap); - INTON; - } - return (0); - } - } - - return (1); -} - -#ifdef mkinit -MKINIT void rmaliases(void); - -SHELLPROC { - rmaliases(); -} -#endif - -void -rmaliases(void) -{ - struct alias *ap, *tmp; - int i; - - INTOFF; - for (i = 0; i < ATABSIZE; i++) { - ap = atab[i]; - atab[i] = NULL; - while (ap) { - ckfree(ap->name); - ckfree(ap->val); - tmp = ap; - ap = ap->next; - ckfree(tmp); - } - } - INTON; -} - -struct alias * -lookupalias(char *name, int check) -{ - struct alias *ap = *hashalias(name); - - for (; ap; ap = ap->next) { - if (equal(name, ap->name)) { - if (check && (ap->flag & ALIASINUSE)) - return (NULL); - return (ap); - } - } - - return (NULL); -} - -char * -get_alias_text(char *name) -{ - struct alias *ap; - - ap = lookupalias(name, 0); - if (ap == NULL) - return NULL; - return ap->val; -} - -/* - * TODO - sort output - */ -int -aliascmd(int argc, char **argv) -{ - char *n, *v; - int ret = 0; - struct alias *ap; - - if (argc == 1) { - int i; - - for (i = 0; i < ATABSIZE; i++) - for (ap = atab[i]; ap; ap = ap->next) { - if (*ap->name != '\0') { - out1fmt("alias %s=", ap->name); - print_quoted(ap->val); - out1c('\n'); - } - } - return (0); - } - while ((n = *++argv) != NULL) { - if ((v = strchr(n+1, '=')) == NULL) { /* n+1: funny ksh stuff */ - if ((ap = lookupalias(n, 0)) == NULL) { - outfmt(out2, "alias: %s not found\n", n); - ret = 1; - } else { - out1fmt("alias %s=", n); - print_quoted(ap->val); - out1c('\n'); - } - } else { - *v++ = '\0'; - setalias(n, v); - } - } - - return (ret); -} - -int -unaliascmd(int argc, char **argv) -{ - int i; - - while ((i = nextopt("a")) != '\0') { - if (i == 'a') { - rmaliases(); - return (0); - } - } - for (i = 0; *argptr; argptr++) - i = unalias(*argptr); - - return (i); -} - -STATIC struct alias ** -hashalias(char *p) -{ - unsigned int hashval; - - hashval = *p << 4; - while (*p) - hashval+= *p++; - return &atab[hashval % ATABSIZE]; -} diff --git a/sh/alias.h b/sh/alias.h deleted file mode 100644 index 7ce25f4..0000000 --- a/sh/alias.h +++ /dev/null @@ -1,50 +0,0 @@ -/* $NetBSD: alias.h,v 1.6 2003/08/07 09:05:29 agc Exp $ */ - -/*- - * Copyright (c) 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * 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. - * - * @(#)alias.h 8.2 (Berkeley) 5/4/95 - */ - -#define ALIASINUSE 1 - -struct alias { - struct alias *next; - char *name; - char *val; - int flag; -}; - -struct alias *lookupalias(char *, int); -char *get_alias_text(char *); -int aliascmd(int, char **); -int unaliascmd(int, char **); -void rmaliases(void); diff --git a/sh/arith.c b/sh/arith.c deleted file mode 100644 index f8f92a9..0000000 --- a/sh/arith.c +++ /dev/null @@ -1,1587 +0,0 @@ -/* A Bison parser, made by GNU Bison 1.875d. */ - -/* Skeleton parser for Yacc-like parsing with Bison, - Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ - -/* As a special exception, when this file is copied by Bison into a - Bison output file, you may use that output file without restriction. - This special exception was added by the Free Software Foundation - in version 1.24 of Bison. */ - -/* Written by Richard Stallman by simplifying the original so called - ``semantic'' parser. */ - -/* All symbols defined below should begin with yy or YY, to avoid - infringing on user name space. This should be done even for local - variables, as they might otherwise be expanded by user macros. - There are some unavoidable exceptions within include files to - define necessary library symbols; they are noted "INFRINGES ON - USER NAME SPACE" below. */ - -/* Identify Bison output. */ -#define YYBISON 1 - -/* Skeleton name. */ -#define YYSKELETON_NAME "yacc.c" - -/* Pure parsers. */ -#define YYPURE 0 - -/* Using locations. */ -#define YYLSP_NEEDED 0 - - - -/* Tokens. */ -#ifndef YYTOKENTYPE -# define YYTOKENTYPE - /* Put the tokens into the symbol table, so that GDB and other debuggers - know about them. */ - enum yytokentype { - ARITH_NUM = 258, - ARITH_LPAREN = 259, - ARITH_RPAREN = 260, - ARITH_OR = 261, - ARITH_AND = 262, - ARITH_BOR = 263, - ARITH_BXOR = 264, - ARITH_BAND = 265, - ARITH_NE = 266, - ARITH_EQ = 267, - ARITH_LE = 268, - ARITH_GE = 269, - ARITH_GT = 270, - ARITH_LT = 271, - ARITH_RSHIFT = 272, - ARITH_LSHIFT = 273, - ARITH_SUB = 274, - ARITH_ADD = 275, - ARITH_REM = 276, - ARITH_DIV = 277, - ARITH_MUL = 278, - ARITH_BNOT = 279, - ARITH_NOT = 280, - ARITH_UNARYPLUS = 281, - ARITH_UNARYMINUS = 282 - }; -#endif -#define ARITH_NUM 258 -#define ARITH_LPAREN 259 -#define ARITH_RPAREN 260 -#define ARITH_OR 261 -#define ARITH_AND 262 -#define ARITH_BOR 263 -#define ARITH_BXOR 264 -#define ARITH_BAND 265 -#define ARITH_NE 266 -#define ARITH_EQ 267 -#define ARITH_LE 268 -#define ARITH_GE 269 -#define ARITH_GT 270 -#define ARITH_LT 271 -#define ARITH_RSHIFT 272 -#define ARITH_LSHIFT 273 -#define ARITH_SUB 274 -#define ARITH_ADD 275 -#define ARITH_REM 276 -#define ARITH_DIV 277 -#define ARITH_MUL 278 -#define ARITH_BNOT 279 -#define ARITH_NOT 280 -#define ARITH_UNARYPLUS 281 -#define ARITH_UNARYMINUS 282 - - - - -/* Copy the first part of user declarations. */ -#line 1 "arith.y" - -/* $NetBSD: arith.y,v 1.17 2003/09/17 17:33:36 jmmv Exp $ */ - -/*- - * Copyright (c) 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * 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[] = "@(#)arith.y 8.3 (Berkeley) 5/4/95"; -#else -__RCSID("$NetBSD: arith.y,v 1.17 2003/09/17 17:33:36 jmmv Exp $"); -#endif -#endif /* not lint */ - -#include <stdlib.h> -#include "expand.h" -#include "shell.h" -#include "error.h" -#include "output.h" -#include "memalloc.h" - -const char *arith_buf, *arith_startbuf; - -void yyerror(const char *); -#ifdef TESTARITH -int main(int , char *[]); -int error(char *); -#endif - - - -/* Enabling traces. */ -#ifndef YYDEBUG -# define YYDEBUG 0 -#endif - -/* Enabling verbose error messages. */ -#ifdef YYERROR_VERBOSE -# undef YYERROR_VERBOSE -# define YYERROR_VERBOSE 1 -#else -# define YYERROR_VERBOSE 0 -#endif - -#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED) -typedef int YYSTYPE; -# define yystype YYSTYPE /* obsolescent; will be withdrawn */ -# define YYSTYPE_IS_DECLARED 1 -# define YYSTYPE_IS_TRIVIAL 1 -#endif - - - -/* Copy the second part of user declarations. */ - - -/* Line 214 of yacc.c. */ -#line 202 "arith.c" - -#if ! defined (yyoverflow) || YYERROR_VERBOSE - -# ifndef YYFREE -# define YYFREE free -# endif -# ifndef YYMALLOC -# define YYMALLOC malloc -# endif - -/* The parser invokes alloca or malloc; define the necessary symbols. */ - -# ifdef YYSTACK_USE_ALLOCA -# if YYSTACK_USE_ALLOCA -# define YYSTACK_ALLOC alloca -# endif -# else -# if defined (alloca) || defined (_ALLOCA_H) -# define YYSTACK_ALLOC alloca -# else -# ifdef __GNUC__ -# define YYSTACK_ALLOC __builtin_alloca -# endif -# endif -# endif - -# ifdef YYSTACK_ALLOC - /* Pacify GCC's `empty if-body' warning. */ -# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) -# else -# if defined (__STDC__) || defined (__cplusplus) -# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ -# define YYSIZE_T size_t -# endif -# define YYSTACK_ALLOC YYMALLOC -# define YYSTACK_FREE YYFREE -# endif -#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */ - - -#if (! defined (yyoverflow) \ - && (! defined (__cplusplus) \ - || (defined (YYSTYPE_IS_TRIVIAL) && YYSTYPE_IS_TRIVIAL))) - -/* A type that is properly aligned for any stack member. */ -union yyalloc -{ - short int yyss; - YYSTYPE yyvs; - }; - -/* The size of the maximum gap between one aligned stack and the next. */ -# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) - -/* The size of an array large to enough to hold all stacks, each with - N elements. */ -# define YYSTACK_BYTES(N) \ - ((N) * (sizeof (short int) + sizeof (YYSTYPE)) \ - + YYSTACK_GAP_MAXIMUM) - -/* Copy COUNT objects from FROM to TO. The source and destination do - not overlap. */ -# ifndef YYCOPY -# if defined (__GNUC__) && 1 < __GNUC__ -# define YYCOPY(To, From, Count) \ - __builtin_memcpy (To, From, (Count) * sizeof (*(From))) -# else -# define YYCOPY(To, From, Count) \ - do \ - { \ - register YYSIZE_T yyi; \ - for (yyi = 0; yyi < (Count); yyi++) \ - (To)[yyi] = (From)[yyi]; \ - } \ - while (0) -# endif -# endif - -/* Relocate STACK from its old location to the new one. The - local variables YYSIZE and YYSTACKSIZE give the old and new number of - elements in the stack, and YYPTR gives the new location of the - stack. Advance YYPTR to a properly aligned location for the next - stack. */ -# define YYSTACK_RELOCATE(Stack) \ - do \ - { \ - YYSIZE_T yynewbytes; \ - YYCOPY (&yyptr->Stack, Stack, yysize); \ - Stack = &yyptr->Stack; \ - yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ - yyptr += yynewbytes / sizeof (*yyptr); \ - } \ - while (0) - -#endif - -#if defined (__STDC__) || defined (__cplusplus) - typedef signed char yysigned_char; -#else - typedef short int yysigned_char; -#endif - -/* YYFINAL -- State number of the termination state. */ -#define YYFINAL 14 -/* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 170 - -/* YYNTOKENS -- Number of terminals. */ -#define YYNTOKENS 28 -/* YYNNTS -- Number of nonterminals. */ -#define YYNNTS 3 -/* YYNRULES -- Number of rules. */ -#define YYNRULES 26 -/* YYNRULES -- Number of states. */ -#define YYNSTATES 52 - -/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ -#define YYUNDEFTOK 2 -#define YYMAXUTOK 282 - -#define YYTRANSLATE(YYX) \ - ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) - -/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ -static const unsigned char yytranslate[] = -{ - 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, - 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27 -}; - -#if YYDEBUG -/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in - YYRHS. */ -static const unsigned char yyprhs[] = -{ - 0, 0, 3, 5, 9, 13, 17, 21, 25, 29, - 33, 37, 41, 45, 49, 53, 57, 61, 65, 69, - 73, 77, 81, 84, 87, 90, 93 -}; - -/* YYRHS -- A `-1'-separated list of the rules' RHS. */ -static const yysigned_char yyrhs[] = -{ - 29, 0, -1, 30, -1, 4, 30, 5, -1, 30, - 6, 30, -1, 30, 7, 30, -1, 30, 8, 30, - -1, 30, 9, 30, -1, 30, 10, 30, -1, 30, - 12, 30, -1, 30, 15, 30, -1, 30, 14, 30, - -1, 30, 16, 30, -1, 30, 13, 30, -1, 30, - 11, 30, -1, 30, 18, 30, -1, 30, 17, 30, - -1, 30, 20, 30, -1, 30, 19, 30, -1, 30, - 23, 30, -1, 30, 22, 30, -1, 30, 21, 30, - -1, 25, 30, -1, 24, 30, -1, 19, 30, -1, - 20, 30, -1, 3, -1 -}; - -/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ -static const unsigned char yyrline[] = -{ - 0, 76, 76, 82, 83, 84, 85, 86, 87, 88, - 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, - 99, 104, 109, 110, 111, 112, 113 -}; -#endif - -#if YYDEBUG || YYERROR_VERBOSE -/* YYTNME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. - First, the terminals, then, starting at YYNTOKENS, nonterminals. */ -static const char *const yytname[] = -{ - "$end", "error", "$undefined", "ARITH_NUM", "ARITH_LPAREN", - "ARITH_RPAREN", "ARITH_OR", "ARITH_AND", "ARITH_BOR", "ARITH_BXOR", - "ARITH_BAND", "ARITH_NE", "ARITH_EQ", "ARITH_LE", "ARITH_GE", "ARITH_GT", - "ARITH_LT", "ARITH_RSHIFT", "ARITH_LSHIFT", "ARITH_SUB", "ARITH_ADD", - "ARITH_REM", "ARITH_DIV", "ARITH_MUL", "ARITH_BNOT", "ARITH_NOT", - "ARITH_UNARYPLUS", "ARITH_UNARYMINUS", "$accept", "exp", "expr", 0 -}; -#endif - -# ifdef YYPRINT -/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to - token YYLEX-NUM. */ -static const unsigned short int yytoknum[] = -{ - 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, - 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, - 275, 276, 277, 278, 279, 280, 281, 282 -}; -# endif - -/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ -static const unsigned char yyr1[] = -{ - 0, 28, 29, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30 -}; - -/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ -static const unsigned char yyr2[] = -{ - 0, 2, 1, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 2, 2, 2, 2, 1 -}; - -/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state - STATE-NUM when YYTABLE doesn't specify something else to do. Zero - means the default is an error. */ -static const unsigned char yydefact[] = -{ - 0, 26, 0, 0, 0, 0, 0, 0, 2, 0, - 24, 25, 23, 22, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 3, 4, 5, 6, 7, 8, 14, - 9, 13, 11, 10, 12, 16, 15, 18, 17, 21, - 20, 19 -}; - -/* YYDEFGOTO[NTERM-NUM]. */ -static const yysigned_char yydefgoto[] = -{ - -1, 7, 8 -}; - -/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing - STATE-NUM. */ -#define YYPACT_NINF -13 -static const short int yypact[] = -{ - 28, -13, 28, 28, 28, 28, 28, 12, 67, 49, - -13, -13, -13, -13, -13, 28, 28, 28, 28, 28, - 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, - 28, 28, 28, -13, 84, 100, 115, 23, 128, 139, - 139, -12, -12, -12, -12, 144, 144, 147, 147, -13, - -13, -13 -}; - -/* YYPGOTO[NTERM-NUM]. */ -static const yysigned_char yypgoto[] = -{ - -13, -13, -2 -}; - -/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If - positive, shift that token. If negative, reduce the rule which - number is the opposite. If zero, do what YYDEFACT says. - If YYTABLE_NINF, syntax error. */ -#define YYTABLE_NINF -1 -static const unsigned char yytable[] = -{ - 9, 10, 11, 12, 13, 26, 27, 28, 29, 30, - 31, 32, 14, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, - 51, 1, 2, 19, 20, 21, 22, 23, 24, 25, - 26, 27, 28, 29, 30, 31, 32, 3, 4, 0, - 0, 0, 5, 6, 33, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, - 30, 31, 32, 15, 16, 17, 18, 19, 20, 21, - 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 30, 31, 32, 17, 18, - 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, - 29, 30, 31, 32, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, 32, 20, - 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, - 31, 32, 22, 23, 24, 25, 26, 27, 28, 29, - 30, 31, 32, 28, 29, 30, 31, 32, 30, 31, - 32 -}; - -static const yysigned_char yycheck[] = -{ - 2, 3, 4, 5, 6, 17, 18, 19, 20, 21, - 22, 23, 0, 15, 16, 17, 18, 19, 20, 21, - 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 3, 4, 10, 11, 12, 13, 14, 15, 16, - 17, 18, 19, 20, 21, 22, 23, 19, 20, -1, - -1, -1, 24, 25, 5, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, - 21, 22, 23, 6, 7, 8, 9, 10, 11, 12, - 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, - 23, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 11, - 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, - 22, 23, 13, 14, 15, 16, 17, 18, 19, 20, - 21, 22, 23, 19, 20, 21, 22, 23, 21, 22, - 23 -}; - -/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing - symbol of state STATE-NUM. */ -static const unsigned char yystos[] = -{ - 0, 3, 4, 19, 20, 24, 25, 29, 30, 30, - 30, 30, 30, 30, 0, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, - 21, 22, 23, 5, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30 -}; - -#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__) -# define YYSIZE_T __SIZE_TYPE__ -#endif -#if ! defined (YYSIZE_T) && defined (size_t) -# define YYSIZE_T size_t -#endif -#if ! defined (YYSIZE_T) -# if defined (__STDC__) || defined (__cplusplus) -# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ -# define YYSIZE_T size_t -# endif -#endif -#if ! defined (YYSIZE_T) -# define YYSIZE_T unsigned int -#endif - -#define yyerrok (yyerrstatus = 0) -#define yyclearin (yychar = YYEMPTY) -#define YYEMPTY (-2) -#define YYEOF 0 - -#define YYACCEPT goto yyacceptlab -#define YYABORT goto yyabortlab -#define YYERROR goto yyerrorlab - - -/* Like YYERROR except do call yyerror. This remains here temporarily - to ease the transition to the new meaning of YYERROR, for GCC. - Once GCC version 2 has supplanted version 1, this can go. */ - -#define YYFAIL goto yyerrlab - -#define YYRECOVERING() (!!yyerrstatus) - -#define YYBACKUP(Token, Value) \ -do \ - if (yychar == YYEMPTY && yylen == 1) \ - { \ - yychar = (Token); \ - yylval = (Value); \ - yytoken = YYTRANSLATE (yychar); \ - YYPOPSTACK; \ - goto yybackup; \ - } \ - else \ - { \ - yyerror ("syntax error: cannot back up");\ - YYERROR; \ - } \ -while (0) - -#define YYTERROR 1 -#define YYERRCODE 256 - -/* YYLLOC_DEFAULT -- Compute the default location (before the actions - are run). */ - -#ifndef YYLLOC_DEFAULT -# define YYLLOC_DEFAULT(Current, Rhs, N) \ - ((Current).first_line = (Rhs)[1].first_line, \ - (Current).first_column = (Rhs)[1].first_column, \ - (Current).last_line = (Rhs)[N].last_line, \ - (Current).last_column = (Rhs)[N].last_column) -#endif - -/* YYLEX -- calling `yylex' with the right arguments. */ - -#ifdef YYLEX_PARAM -# define YYLEX yylex (YYLEX_PARAM) -#else -# define YYLEX yylex () -#endif - -/* Enable debugging if requested. */ -#if YYDEBUG - -# ifndef YYFPRINTF -# include <stdio.h> /* INFRINGES ON USER NAME SPACE */ -# define YYFPRINTF fprintf -# endif - -# define YYDPRINTF(Args) \ -do { \ - if (yydebug) \ - YYFPRINTF Args; \ -} while (0) - -# define YYDSYMPRINT(Args) \ -do { \ - if (yydebug) \ - yysymprint Args; \ -} while (0) - -# define YYDSYMPRINTF(Title, Token, Value, Location) \ -do { \ - if (yydebug) \ - { \ - YYFPRINTF (stderr, "%s ", Title); \ - yysymprint (stderr, \ - Token, Value); \ - YYFPRINTF (stderr, "\n"); \ - } \ -} while (0) - -/*------------------------------------------------------------------. -| yy_stack_print -- Print the state stack from its BOTTOM up to its | -| TOP (included). | -`------------------------------------------------------------------*/ - -#if defined (__STDC__) || defined (__cplusplus) -static void -yy_stack_print (short int *bottom, short int *top) -#else -static void -yy_stack_print (bottom, top) - short int *bottom; - short int *top; -#endif -{ - YYFPRINTF (stderr, "Stack now"); - for (/* Nothing. */; bottom <= top; ++bottom) - YYFPRINTF (stderr, " %d", *bottom); - YYFPRINTF (stderr, "\n"); -} - -# define YY_STACK_PRINT(Bottom, Top) \ -do { \ - if (yydebug) \ - yy_stack_print ((Bottom), (Top)); \ -} while (0) - - -/*------------------------------------------------. -| Report that the YYRULE is going to be reduced. | -`------------------------------------------------*/ - -#if defined (__STDC__) || defined (__cplusplus) -static void -yy_reduce_print (int yyrule) -#else -static void -yy_reduce_print (yyrule) - int yyrule; -#endif -{ - int yyi; - unsigned int yylno = yyrline[yyrule]; - YYFPRINTF (stderr, "Reducing stack by rule %d (line %u), ", - yyrule - 1, yylno); - /* Print the symbols being reduced, and their result. */ - for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++) - YYFPRINTF (stderr, "%s ", yytname [yyrhs[yyi]]); - YYFPRINTF (stderr, "-> %s\n", yytname [yyr1[yyrule]]); -} - -# define YY_REDUCE_PRINT(Rule) \ -do { \ - if (yydebug) \ - yy_reduce_print (Rule); \ -} while (0) - -/* Nonzero means print parse trace. It is left uninitialized so that - multiple parsers can coexist. */ -int yydebug; -#else /* !YYDEBUG */ -# define YYDPRINTF(Args) -# define YYDSYMPRINT(Args) -# define YYDSYMPRINTF(Title, Token, Value, Location) -# define YY_STACK_PRINT(Bottom, Top) -# define YY_REDUCE_PRINT(Rule) -#endif /* !YYDEBUG */ - - -/* YYINITDEPTH -- initial size of the parser's stacks. */ -#ifndef YYINITDEPTH -# define YYINITDEPTH 200 -#endif - -/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only - if the built-in stack extension method is used). - - Do not make this value too large; the results are undefined if - SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH) - evaluated with infinite-precision integer arithmetic. */ - -#if defined (YYMAXDEPTH) && YYMAXDEPTH == 0 -# undef YYMAXDEPTH -#endif - -#ifndef YYMAXDEPTH -# define YYMAXDEPTH 10000 -#endif - - - -#if YYERROR_VERBOSE - -# ifndef yystrlen -# if defined (__GLIBC__) && defined (_STRING_H) -# define yystrlen strlen -# else -/* Return the length of YYSTR. */ -static YYSIZE_T -# if defined (__STDC__) || defined (__cplusplus) -yystrlen (const char *yystr) -# else -yystrlen (yystr) - const char *yystr; -# endif -{ - register const char *yys = yystr; - - while (*yys++ != '\0') - continue; - - return yys - yystr - 1; -} -# endif -# endif - -# ifndef yystpcpy -# if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE) -# define yystpcpy stpcpy -# else -/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in - YYDEST. */ -static char * -# if defined (__STDC__) || defined (__cplusplus) -yystpcpy (char *yydest, const char *yysrc) -# else -yystpcpy (yydest, yysrc) - char *yydest; - const char *yysrc; -# endif -{ - register char *yyd = yydest; - register const char *yys = yysrc; - - while ((*yyd++ = *yys++) != '\0') - continue; - - return yyd - 1; -} -# endif -# endif - -#endif /* !YYERROR_VERBOSE */ - - - -#if YYDEBUG -/*--------------------------------. -| Print this symbol on YYOUTPUT. | -`--------------------------------*/ - -#if defined (__STDC__) || defined (__cplusplus) -static void -yysymprint (FILE *yyoutput, int yytype, YYSTYPE *yyvaluep) -#else -static void -yysymprint (yyoutput, yytype, yyvaluep) - FILE *yyoutput; - int yytype; - YYSTYPE *yyvaluep; -#endif -{ - /* Pacify ``unused variable'' warnings. */ - (void) yyvaluep; - - if (yytype < YYNTOKENS) - { - YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); -# ifdef YYPRINT - YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); -# endif - } - else - YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); - - switch (yytype) - { - default: - break; - } - YYFPRINTF (yyoutput, ")"); -} - -#endif /* ! YYDEBUG */ -/*-----------------------------------------------. -| Release the memory associated to this symbol. | -`-----------------------------------------------*/ - -#if defined (__STDC__) || defined (__cplusplus) -static void -yydestruct (int yytype, YYSTYPE *yyvaluep) -#else -static void -yydestruct (yytype, yyvaluep) - int yytype; - YYSTYPE *yyvaluep; -#endif -{ - /* Pacify ``unused variable'' warnings. */ - (void) yyvaluep; - - switch (yytype) - { - - default: - break; - } -} - - -/* Prevent warnings from -Wmissing-prototypes. */ - -#ifdef YYPARSE_PARAM -# if defined (__STDC__) || defined (__cplusplus) -int yyparse (void *YYPARSE_PARAM); -# else -int yyparse (); -# endif -#else /* ! YYPARSE_PARAM */ -#if defined (__STDC__) || defined (__cplusplus) -int yyparse (void); -#else -int yyparse (); -#endif -#endif /* ! YYPARSE_PARAM */ - - - -/* The lookahead symbol. */ -int yychar; - -/* The semantic value of the lookahead symbol. */ -YYSTYPE yylval; - -/* Number of syntax errors so far. */ -int yynerrs; - - - -/*----------. -| yyparse. | -`----------*/ - -#ifdef YYPARSE_PARAM -# if defined (__STDC__) || defined (__cplusplus) -int yyparse (void *YYPARSE_PARAM) -# else -int yyparse (YYPARSE_PARAM) - void *YYPARSE_PARAM; -# endif -#else /* ! YYPARSE_PARAM */ -#if defined (__STDC__) || defined (__cplusplus) -int -yyparse (void) -#else -int -yyparse () - -#endif -#endif -{ - - register int yystate; - register int yyn; - int yyresult; - /* Number of tokens to shift before error messages enabled. */ - int yyerrstatus; - /* Lookahead token as an internal (translated) token number. */ - int yytoken = 0; - - /* Three stacks and their tools: - `yyss': related to states, - `yyvs': related to semantic values, - `yyls': related to locations. - - Refer to the stacks thru separate pointers, to allow yyoverflow - to reallocate them elsewhere. */ - - /* The state stack. */ - short int yyssa[YYINITDEPTH]; - short int *yyss = yyssa; - register short int *yyssp; - - /* The semantic value stack. */ - YYSTYPE yyvsa[YYINITDEPTH]; - YYSTYPE *yyvs = yyvsa; - register YYSTYPE *yyvsp; - - - -#define YYPOPSTACK (yyvsp--, yyssp--) - - YYSIZE_T yystacksize = YYINITDEPTH; - - /* The variables used to return semantic value and location from the - action routines. */ - YYSTYPE yyval; - - - /* When reducing, the number of symbols on the RHS of the reduced - rule. */ - int yylen; - - YYDPRINTF ((stderr, "Starting parse\n")); - - yystate = 0; - yyerrstatus = 0; - yynerrs = 0; - yychar = YYEMPTY; /* Cause a token to be read. */ - - /* Initialize stack pointers. - Waste one element of value and location stack - so that they stay on the same level as the state stack. - The wasted elements are never initialized. */ - - yyssp = yyss; - yyvsp = yyvs; - - - goto yysetstate; - -/*------------------------------------------------------------. -| yynewstate -- Push a new state, which is found in yystate. | -`------------------------------------------------------------*/ - yynewstate: - /* In all cases, when you get here, the value and location stacks - have just been pushed. so pushing a state here evens the stacks. - */ - yyssp++; - - yysetstate: - *yyssp = yystate; - - if (yyss + yystacksize - 1 <= yyssp) - { - /* Get the current used size of the three stacks, in elements. */ - YYSIZE_T yysize = yyssp - yyss + 1; - -#ifdef yyoverflow - { - /* Give user a chance to reallocate the stack. Use copies of - these so that the &'s don't force the real ones into - memory. */ - YYSTYPE *yyvs1 = yyvs; - short int *yyss1 = yyss; - - - /* Each stack pointer address is followed by the size of the - data in use in that stack, in bytes. This used to be a - conditional around just the two extra args, but that might - be undefined if yyoverflow is a macro. */ - yyoverflow ("parser stack overflow", - &yyss1, yysize * sizeof (*yyssp), - &yyvs1, yysize * sizeof (*yyvsp), - - &yystacksize); - - yyss = yyss1; - yyvs = yyvs1; - } -#else /* no yyoverflow */ -# ifndef YYSTACK_RELOCATE - goto yyoverflowlab; -# else - /* Extend the stack our own way. */ - if (YYMAXDEPTH <= yystacksize) - goto yyoverflowlab; - yystacksize *= 2; - if (YYMAXDEPTH < yystacksize) - yystacksize = YYMAXDEPTH; - - { - short int *yyss1 = yyss; - union yyalloc *yyptr = - (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); - if (! yyptr) - goto yyoverflowlab; - YYSTACK_RELOCATE (yyss); - YYSTACK_RELOCATE (yyvs); - -# undef YYSTACK_RELOCATE - if (yyss1 != yyssa) - YYSTACK_FREE (yyss1); - } -# endif -#endif /* no yyoverflow */ - - yyssp = yyss + yysize - 1; - yyvsp = yyvs + yysize - 1; - - - YYDPRINTF ((stderr, "Stack size increased to %lu\n", - (unsigned long int) yystacksize)); - - if (yyss + yystacksize - 1 <= yyssp) - YYABORT; - } - - YYDPRINTF ((stderr, "Entering state %d\n", yystate)); - - goto yybackup; - -/*-----------. -| yybackup. | -`-----------*/ -yybackup: - -/* Do appropriate processing given the current state. */ -/* Read a lookahead token if we need one and don't already have one. */ -/* yyresume: */ - - /* First try to decide what to do without reference to lookahead token. */ - - yyn = yypact[yystate]; - if (yyn == YYPACT_NINF) - goto yydefault; - - /* Not known => get a lookahead token if don't already have one. */ - - /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ - if (yychar == YYEMPTY) - { - YYDPRINTF ((stderr, "Reading a token: ")); - yychar = YYLEX; - } - - if (yychar <= YYEOF) - { - yychar = yytoken = YYEOF; - YYDPRINTF ((stderr, "Now at end of input.\n")); - } - else - { - yytoken = YYTRANSLATE (yychar); - YYDSYMPRINTF ("Next token is", yytoken, &yylval, &yylloc); - } - - /* If the proper action on seeing token YYTOKEN is to reduce or to - detect an error, take that action. */ - yyn += yytoken; - if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) - goto yydefault; - yyn = yytable[yyn]; - if (yyn <= 0) - { - if (yyn == 0 || yyn == YYTABLE_NINF) - goto yyerrlab; - yyn = -yyn; - goto yyreduce; - } - - if (yyn == YYFINAL) - YYACCEPT; - - /* Shift the lookahead token. */ - YYDPRINTF ((stderr, "Shifting token %s, ", yytname[yytoken])); - - /* Discard the token being shifted unless it is eof. */ - if (yychar != YYEOF) - yychar = YYEMPTY; - - *++yyvsp = yylval; - - - /* Count tokens shifted since error; after three, turn off error - status. */ - if (yyerrstatus) - yyerrstatus--; - - yystate = yyn; - goto yynewstate; - - -/*-----------------------------------------------------------. -| yydefault -- do the default action for the current state. | -`-----------------------------------------------------------*/ -yydefault: - yyn = yydefact[yystate]; - if (yyn == 0) - goto yyerrlab; - goto yyreduce; - - -/*-----------------------------. -| yyreduce -- Do a reduction. | -`-----------------------------*/ -yyreduce: - /* yyn is the number of a rule to reduce with. */ - yylen = yyr2[yyn]; - - /* If YYLEN is nonzero, implement the default value of the action: - `$$ = $1'. - - Otherwise, the following line sets YYVAL to garbage. - This behavior is undocumented and Bison - users should not rely upon it. Assigning to YYVAL - unconditionally makes the parser a bit smaller, and it avoids a - GCC warning that YYVAL may be used uninitialized. */ - yyval = yyvsp[1-yylen]; - - - YY_REDUCE_PRINT (yyn); - switch (yyn) - { - case 2: -#line 76 "arith.y" - { - return (yyvsp[0]); - ;} - break; - - case 3: -#line 82 "arith.y" - { yyval = yyvsp[-1]; ;} - break; - - case 4: -#line 83 "arith.y" - { yyval = yyvsp[-2] ? yyvsp[-2] : yyvsp[0] ? yyvsp[0] : 0; ;} - break; - - case 5: -#line 84 "arith.y" - { yyval = yyvsp[-2] ? ( yyvsp[0] ? yyvsp[0] : 0 ) : 0; ;} - break; - - case 6: -#line 85 "arith.y" - { yyval = yyvsp[-2] | yyvsp[0]; ;} - break; - - case 7: -#line 86 "arith.y" - { yyval = yyvsp[-2] ^ yyvsp[0]; ;} - break; - - case 8: -#line 87 "arith.y" - { yyval = yyvsp[-2] & yyvsp[0]; ;} - break; - - case 9: -#line 88 "arith.y" - { yyval = yyvsp[-2] == yyvsp[0]; ;} - break; - - case 10: -#line 89 "arith.y" - { yyval = yyvsp[-2] > yyvsp[0]; ;} - break; - - case 11: -#line 90 "arith.y" - { yyval = yyvsp[-2] >= yyvsp[0]; ;} - break; - - case 12: -#line 91 "arith.y" - { yyval = yyvsp[-2] < yyvsp[0]; ;} - break; - - case 13: -#line 92 "arith.y" - { yyval = yyvsp[-2] <= yyvsp[0]; ;} - break; - - case 14: -#line 93 "arith.y" - { yyval = yyvsp[-2] != yyvsp[0]; ;} - break; - - case 15: -#line 94 "arith.y" - { yyval = yyvsp[-2] << yyvsp[0]; ;} - break; - - case 16: -#line 95 "arith.y" - { yyval = yyvsp[-2] >> yyvsp[0]; ;} - break; - - case 17: -#line 96 "arith.y" - { yyval = yyvsp[-2] + yyvsp[0]; ;} - break; - - case 18: -#line 97 "arith.y" - { yyval = yyvsp[-2] - yyvsp[0]; ;} - break; - - case 19: -#line 98 "arith.y" - { yyval = yyvsp[-2] * yyvsp[0]; ;} - break; - - case 20: -#line 99 "arith.y" - { - if (yyvsp[0] == 0) - yyerror("division by zero"); - yyval = yyvsp[-2] / yyvsp[0]; - ;} - break; - - case 21: -#line 104 "arith.y" - { - if (yyvsp[0] == 0) - yyerror("division by zero"); - yyval = yyvsp[-2] % yyvsp[0]; - ;} - break; - - case 22: -#line 109 "arith.y" - { yyval = !(yyvsp[0]); ;} - break; - - case 23: -#line 110 "arith.y" - { yyval = ~(yyvsp[0]); ;} - break; - - case 24: -#line 111 "arith.y" - { yyval = -(yyvsp[0]); ;} - break; - - case 25: -#line 112 "arith.y" - { yyval = yyvsp[0]; ;} - break; - - - } - -/* Line 1010 of yacc.c. */ -#line 1276 "arith.c" - - yyvsp -= yylen; - yyssp -= yylen; - - - YY_STACK_PRINT (yyss, yyssp); - - *++yyvsp = yyval; - - - /* Now `shift' the result of the reduction. Determine what state - that goes to, based on the state we popped back to and the rule - number reduced by. */ - - yyn = yyr1[yyn]; - - yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; - if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) - yystate = yytable[yystate]; - else - yystate = yydefgoto[yyn - YYNTOKENS]; - - goto yynewstate; - - -/*------------------------------------. -| yyerrlab -- here on detecting error | -`------------------------------------*/ -yyerrlab: - /* If not already recovering from an error, report this error. */ - if (!yyerrstatus) - { - ++yynerrs; -#if YYERROR_VERBOSE - yyn = yypact[yystate]; - - if (YYPACT_NINF < yyn && yyn < YYLAST) - { - YYSIZE_T yysize = 0; - int yytype = YYTRANSLATE (yychar); - const char* yyprefix; - char *yymsg; - int yyx; - - /* Start YYX at -YYN if negative to avoid negative indexes in - YYCHECK. */ - int yyxbegin = yyn < 0 ? -yyn : 0; - - /* Stay within bounds of both yycheck and yytname. */ - int yychecklim = YYLAST - yyn; - int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; - int yycount = 0; - - yyprefix = ", expecting "; - for (yyx = yyxbegin; yyx < yyxend; ++yyx) - if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) - { - yysize += yystrlen (yyprefix) + yystrlen (yytname [yyx]); - yycount += 1; - if (yycount == 5) - { - yysize = 0; - break; - } - } - yysize += (sizeof ("syntax error, unexpected ") - + yystrlen (yytname[yytype])); - yymsg = (char *) YYSTACK_ALLOC (yysize); - if (yymsg != 0) - { - char *yyp = yystpcpy (yymsg, "syntax error, unexpected "); - yyp = yystpcpy (yyp, yytname[yytype]); - - if (yycount < 5) - { - yyprefix = ", expecting "; - for (yyx = yyxbegin; yyx < yyxend; ++yyx) - if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) - { - yyp = yystpcpy (yyp, yyprefix); - yyp = yystpcpy (yyp, yytname[yyx]); - yyprefix = " or "; - } - } - yyerror (yymsg); - YYSTACK_FREE (yymsg); - } - else - yyerror ("syntax error; also virtual memory exhausted"); - } - else -#endif /* YYERROR_VERBOSE */ - yyerror ("syntax error"); - } - - - - if (yyerrstatus == 3) - { - /* If just tried and failed to reuse lookahead token after an - error, discard it. */ - - if (yychar <= YYEOF) - { - /* If at end of input, pop the error token, - then the rest of the stack, then return failure. */ - if (yychar == YYEOF) - for (;;) - { - YYPOPSTACK; - if (yyssp == yyss) - YYABORT; - YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp); - yydestruct (yystos[*yyssp], yyvsp); - } - } - else - { - YYDSYMPRINTF ("Error: discarding", yytoken, &yylval, &yylloc); - yydestruct (yytoken, &yylval); - yychar = YYEMPTY; - - } - } - - /* Else will try to reuse lookahead token after shifting the error - token. */ - goto yyerrlab1; - - -/*---------------------------------------------------. -| yyerrorlab -- error raised explicitly by YYERROR. | -`---------------------------------------------------*/ -yyerrorlab: - -#ifdef __GNUC__ - /* Pacify GCC when the user code never invokes YYERROR and the label - yyerrorlab therefore never appears in user code. */ - if (0) - goto yyerrorlab; -#endif - - yyvsp -= yylen; - yyssp -= yylen; - yystate = *yyssp; - goto yyerrlab1; - - -/*-------------------------------------------------------------. -| yyerrlab1 -- common code for both syntax error and YYERROR. | -`-------------------------------------------------------------*/ -yyerrlab1: - yyerrstatus = 3; /* Each real token shifted decrements this. */ - - for (;;) - { - yyn = yypact[yystate]; - if (yyn != YYPACT_NINF) - { - yyn += YYTERROR; - if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) - { - yyn = yytable[yyn]; - if (0 < yyn) - break; - } - } - - /* Pop the current state because it cannot handle the error token. */ - if (yyssp == yyss) - YYABORT; - - YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp); - yydestruct (yystos[yystate], yyvsp); - YYPOPSTACK; - yystate = *yyssp; - YY_STACK_PRINT (yyss, yyssp); - } - - if (yyn == YYFINAL) - YYACCEPT; - - YYDPRINTF ((stderr, "Shifting error token, ")); - - *++yyvsp = yylval; - - - yystate = yyn; - goto yynewstate; - - -/*-------------------------------------. -| yyacceptlab -- YYACCEPT comes here. | -`-------------------------------------*/ -yyacceptlab: - yyresult = 0; - goto yyreturn; - -/*-----------------------------------. -| yyabortlab -- YYABORT comes here. | -`-----------------------------------*/ -yyabortlab: - yyresult = 1; - goto yyreturn; - -#ifndef yyoverflow -/*----------------------------------------------. -| yyoverflowlab -- parser overflow comes here. | -`----------------------------------------------*/ -yyoverflowlab: - yyerror ("parser stack overflow"); - yyresult = 2; - /* Fall through. */ -#endif - -yyreturn: -#ifndef yyoverflow - if (yyss != yyssa) - YYSTACK_FREE (yyss); -#endif - return yyresult; -} - - -#line 115 "arith.y" - -int -arith(s) - const char *s; -{ - long result; - - arith_buf = arith_startbuf = s; - - INTOFF; - result = yyparse(); - arith_lex_reset(); /* reprime lex */ - INTON; - - return (result); -} - - -/* - * The exp(1) builtin. - */ -int -expcmd(argc, argv) - int argc; - char **argv; -{ - const char *p; - char *concat; - char **ap; - long i; - - if (argc > 1) { - p = argv[1]; - if (argc > 2) { - /* - * concatenate arguments - */ - STARTSTACKSTR(concat); - ap = argv + 2; - for (;;) { - while (*p) - STPUTC(*p++, concat); - if ((p = *ap++) == NULL) - break; - STPUTC(' ', concat); - } - STPUTC('\0', concat); - p = grabstackstr(concat); - } - } else - p = ""; - - i = arith(p); - - out1fmt("%ld\n", i); - return (! i); -} - -/*************************/ -#ifdef TEST_ARITH -#include <stdio.h> -main(argc, argv) - char *argv[]; -{ - printf("%d\n", exp(argv[1])); -} -error(s) - char *s; -{ - fprintf(stderr, "exp: %s\n", s); - exit(1); -} -#endif - -void -yyerror(s) - const char *s; -{ - -// yyerrok; - yyclearin; - arith_lex_reset(); /* reprime lex */ - error("arithmetic expression: %s: \"%s\"", s, arith_startbuf); - /* NOTREACHED */ -} - - diff --git a/sh/arith.h b/sh/arith.h deleted file mode 100644 index f70c093..0000000 --- a/sh/arith.h +++ /dev/null @@ -1,25 +0,0 @@ -#define ARITH_NUM 258 -#define ARITH_LPAREN 259 -#define ARITH_RPAREN 260 -#define ARITH_OR 261 -#define ARITH_AND 262 -#define ARITH_BOR 263 -#define ARITH_BXOR 264 -#define ARITH_BAND 265 -#define ARITH_NE 266 -#define ARITH_EQ 267 -#define ARITH_LE 268 -#define ARITH_GE 269 -#define ARITH_GT 270 -#define ARITH_LT 271 -#define ARITH_RSHIFT 272 -#define ARITH_LSHIFT 273 -#define ARITH_SUB 274 -#define ARITH_ADD 275 -#define ARITH_REM 276 -#define ARITH_DIV 277 -#define ARITH_MUL 278 -#define ARITH_BNOT 279 -#define ARITH_NOT 280 -#define ARITH_UNARYPLUS 281 -#define ARITH_UNARYMINUS 282 diff --git a/sh/arith.y b/sh/arith.y deleted file mode 100644 index d51ed38..0000000 --- a/sh/arith.y +++ /dev/null @@ -1,199 +0,0 @@ -%{ -/* $NetBSD: arith.y,v 1.17 2003/09/17 17:33:36 jmmv Exp $ */ - -/*- - * Copyright (c) 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * 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[] = "@(#)arith.y 8.3 (Berkeley) 5/4/95"; -#else -__RCSID("$NetBSD: arith.y,v 1.17 2003/09/17 17:33:36 jmmv Exp $"); -#endif -#endif /* not lint */ - -#include <stdlib.h> -#include "expand.h" -#include "shell.h" -#include "error.h" -#include "output.h" -#include "memalloc.h" - -const char *arith_buf, *arith_startbuf; - -void yyerror(const char *); -#ifdef TESTARITH -int main(int , char *[]); -int error(char *); -#endif - -%} -%token ARITH_NUM ARITH_LPAREN ARITH_RPAREN - -%left ARITH_OR -%left ARITH_AND -%left ARITH_BOR -%left ARITH_BXOR -%left ARITH_BAND -%left ARITH_EQ ARITH_NE -%left ARITH_LT ARITH_GT ARITH_GE ARITH_LE -%left ARITH_LSHIFT ARITH_RSHIFT -%left ARITH_ADD ARITH_SUB -%left ARITH_MUL ARITH_DIV ARITH_REM -%left ARITH_UNARYMINUS ARITH_UNARYPLUS ARITH_NOT ARITH_BNOT -%% - -exp: expr { - return ($1); - } - ; - - -expr: ARITH_LPAREN expr ARITH_RPAREN { $$ = $2; } - | expr ARITH_OR expr { $$ = $1 ? $1 : $3 ? $3 : 0; } - | expr ARITH_AND expr { $$ = $1 ? ( $3 ? $3 : 0 ) : 0; } - | expr ARITH_BOR expr { $$ = $1 | $3; } - | expr ARITH_BXOR expr { $$ = $1 ^ $3; } - | expr ARITH_BAND expr { $$ = $1 & $3; } - | expr ARITH_EQ expr { $$ = $1 == $3; } - | expr ARITH_GT expr { $$ = $1 > $3; } - | expr ARITH_GE expr { $$ = $1 >= $3; } - | expr ARITH_LT expr { $$ = $1 < $3; } - | expr ARITH_LE expr { $$ = $1 <= $3; } - | expr ARITH_NE expr { $$ = $1 != $3; } - | expr ARITH_LSHIFT expr { $$ = $1 << $3; } - | expr ARITH_RSHIFT expr { $$ = $1 >> $3; } - | expr ARITH_ADD expr { $$ = $1 + $3; } - | expr ARITH_SUB expr { $$ = $1 - $3; } - | expr ARITH_MUL expr { $$ = $1 * $3; } - | expr ARITH_DIV expr { - if ($3 == 0) - yyerror("division by zero"); - $$ = $1 / $3; - } - | expr ARITH_REM expr { - if ($3 == 0) - yyerror("division by zero"); - $$ = $1 % $3; - } - | ARITH_NOT expr { $$ = !($2); } - | ARITH_BNOT expr { $$ = ~($2); } - | ARITH_SUB expr %prec ARITH_UNARYMINUS { $$ = -($2); } - | ARITH_ADD expr %prec ARITH_UNARYPLUS { $$ = $2; } - | ARITH_NUM - ; -%% -int -arith(s) - const char *s; -{ - long result; - - arith_buf = arith_startbuf = s; - - INTOFF; - result = yyparse(); - arith_lex_reset(); /* reprime lex */ - INTON; - - return (result); -} - - -/* - * The exp(1) builtin. - */ -int -expcmd(argc, argv) - int argc; - char **argv; -{ - const char *p; - char *concat; - char **ap; - long i; - - if (argc > 1) { - p = argv[1]; - if (argc > 2) { - /* - * concatenate arguments - */ - STARTSTACKSTR(concat); - ap = argv + 2; - for (;;) { - while (*p) - STPUTC(*p++, concat); - if ((p = *ap++) == NULL) - break; - STPUTC(' ', concat); - } - STPUTC('\0', concat); - p = grabstackstr(concat); - } - } else - p = ""; - - i = arith(p); - - out1fmt("%ld\n", i); - return (! i); -} - -/*************************/ -#ifdef TEST_ARITH -#include <stdio.h> -main(argc, argv) - char *argv[]; -{ - printf("%d\n", exp(argv[1])); -} -error(s) - char *s; -{ - fprintf(stderr, "exp: %s\n", s); - exit(1); -} -#endif - -void -yyerror(s) - const char *s; -{ - -// yyerrok; - yyclearin; - arith_lex_reset(); /* reprime lex */ - error("arithmetic expression: %s: \"%s\"", s, arith_startbuf); - /* NOTREACHED */ -} diff --git a/sh/arith_lex.c b/sh/arith_lex.c deleted file mode 100644 index 9a132dd..0000000 --- a/sh/arith_lex.c +++ /dev/null @@ -1,1890 +0,0 @@ -#line 2 "arith_lex.c" - -#line 4 "arith_lex.c" - -#define YY_INT_ALIGNED short int - -/* A lexical scanner generated by flex */ - -#define FLEX_SCANNER -#define YY_FLEX_MAJOR_VERSION 2 -#define YY_FLEX_MINOR_VERSION 5 -#define YY_FLEX_SUBMINOR_VERSION 31 -#if YY_FLEX_SUBMINOR_VERSION > 0 -#define FLEX_BETA -#endif - -/* First, we deal with platform-specific or compiler-specific issues. */ - -/* begin standard C headers. */ -#include <stdio.h> -#include <string.h> -#include <errno.h> -#include <stdlib.h> - -/* end standard C headers. */ - -/* flex integer type definitions */ - -#ifndef FLEXINT_H -#define FLEXINT_H - -/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */ - -#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L -#include <inttypes.h> -typedef int8_t flex_int8_t; -typedef uint8_t flex_uint8_t; -typedef int16_t flex_int16_t; -typedef uint16_t flex_uint16_t; -typedef int32_t flex_int32_t; -typedef uint32_t flex_uint32_t; -#else -typedef signed char flex_int8_t; -typedef short int flex_int16_t; -typedef int flex_int32_t; -typedef unsigned char flex_uint8_t; -typedef unsigned short int flex_uint16_t; -typedef unsigned int flex_uint32_t; -#endif /* ! C99 */ - -/* Limits of integral types. */ -#ifndef INT8_MIN -#define INT8_MIN (-128) -#endif -#ifndef INT16_MIN -#define INT16_MIN (-32767-1) -#endif -#ifndef INT32_MIN -#define INT32_MIN (-2147483647-1) -#endif -#ifndef INT8_MAX -#define INT8_MAX (127) -#endif -#ifndef INT16_MAX -#define INT16_MAX (32767) -#endif -#ifndef INT32_MAX -#define INT32_MAX (2147483647) -#endif -#ifndef UINT8_MAX -#define UINT8_MAX (255U) -#endif -#ifndef UINT16_MAX -#define UINT16_MAX (65535U) -#endif -#ifndef UINT32_MAX -#define UINT32_MAX (4294967295U) -#endif - -#endif /* ! FLEXINT_H */ - -#ifdef __cplusplus - -/* The "const" storage-class-modifier is valid. */ -#define YY_USE_CONST - -#else /* ! __cplusplus */ - -#if __STDC__ - -#define YY_USE_CONST - -#endif /* __STDC__ */ -#endif /* ! __cplusplus */ - -#ifdef YY_USE_CONST -#define yyconst const -#else -#define yyconst -#endif - -/* Returned upon end-of-file. */ -#define YY_NULL 0 - -/* Promotes a possibly negative, possibly signed char to an unsigned - * integer for use as an array index. If the signed char is negative, - * we want to instead treat it as an 8-bit unsigned char, hence the - * double cast. - */ -#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) - -/* Enter a start condition. This macro really ought to take a parameter, - * but we do it the disgusting crufty way forced on us by the ()-less - * definition of BEGIN. - */ -#define BEGIN (yy_start) = 1 + 2 * - -/* Translate the current start state into a value that can be later handed - * to BEGIN to return to the state. The YYSTATE alias is for lex - * compatibility. - */ -#define YY_START (((yy_start) - 1) / 2) -#define YYSTATE YY_START - -/* Action number for EOF rule of a given start state. */ -#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) - -/* Special action meaning "start processing a new file". */ -#define YY_NEW_FILE yyrestart(yyin ) - -#define YY_END_OF_BUFFER_CHAR 0 - -/* Size of default input buffer. */ -#ifndef YY_BUF_SIZE -#define YY_BUF_SIZE 16384 -#endif - -#ifndef YY_TYPEDEF_YY_BUFFER_STATE -#define YY_TYPEDEF_YY_BUFFER_STATE -typedef struct yy_buffer_state *YY_BUFFER_STATE; -#endif - -extern int yyleng; - -extern FILE *yyin, *yyout; - -#define EOB_ACT_CONTINUE_SCAN 0 -#define EOB_ACT_END_OF_FILE 1 -#define EOB_ACT_LAST_MATCH 2 - - #define YY_LESS_LINENO(n) - -/* Return all but the first "n" matched characters back to the input stream. */ -#define yyless(n) \ - do \ - { \ - /* Undo effects of setting up yytext. */ \ - int yyless_macro_arg = (n); \ - YY_LESS_LINENO(yyless_macro_arg);\ - *yy_cp = (yy_hold_char); \ - YY_RESTORE_YY_MORE_OFFSET \ - (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ - YY_DO_BEFORE_ACTION; /* set up yytext again */ \ - } \ - while ( 0 ) - -#define unput(c) yyunput( c, (yytext_ptr) ) - -/* The following is because we cannot portably get our hands on size_t - * (without autoconf's help, which isn't available because we want - * flex-generated scanners to compile on their own). - */ - -#ifndef YY_TYPEDEF_YY_SIZE_T -#define YY_TYPEDEF_YY_SIZE_T -typedef unsigned int yy_size_t; -#endif - -#ifndef YY_STRUCT_YY_BUFFER_STATE -#define YY_STRUCT_YY_BUFFER_STATE -struct yy_buffer_state - { - FILE *yy_input_file; - - char *yy_ch_buf; /* input buffer */ - char *yy_buf_pos; /* current position in input buffer */ - - /* Size of input buffer in bytes, not including room for EOB - * characters. - */ - yy_size_t yy_buf_size; - - /* Number of characters read into yy_ch_buf, not including EOB - * characters. - */ - int yy_n_chars; - - /* Whether we "own" the buffer - i.e., we know we created it, - * and can realloc() it to grow it, and should free() it to - * delete it. - */ - int yy_is_our_buffer; - - /* Whether this is an "interactive" input source; if so, and - * if we're using stdio for input, then we want to use getc() - * instead of fread(), to make sure we stop fetching input after - * each newline. - */ - int yy_is_interactive; - - /* Whether we're considered to be at the beginning of a line. - * If so, '^' rules will be active on the next match, otherwise - * not. - */ - int yy_at_bol; - - int yy_bs_lineno; /**< The line count. */ - int yy_bs_column; /**< The column count. */ - - /* Whether to try to fill the input buffer when we reach the - * end of it. - */ - int yy_fill_buffer; - - int yy_buffer_status; - -#define YY_BUFFER_NEW 0 -#define YY_BUFFER_NORMAL 1 - /* When an EOF's been seen but there's still some text to process - * then we mark the buffer as YY_EOF_PENDING, to indicate that we - * shouldn't try reading from the input source any more. We might - * still have a bunch of tokens to match, though, because of - * possible backing-up. - * - * When we actually see the EOF, we change the status to "new" - * (via yyrestart()), so that the user can continue scanning by - * just pointing yyin at a new input file. - */ -#define YY_BUFFER_EOF_PENDING 2 - - }; -#endif /* !YY_STRUCT_YY_BUFFER_STATE */ - -/* Stack of input buffers. */ -static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ -static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ -static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ - -/* We provide macros for accessing buffer states in case in the - * future we want to put the buffer states in a more general - * "scanner state". - * - * Returns the top of the stack, or NULL. - */ -#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ - ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ - : NULL) - -/* Same as previous macro, but useful when we know that the buffer stack is not - * NULL or when we need an lvalue. For internal use only. - */ -#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] - -/* yy_hold_char holds the character lost when yytext is formed. */ -static char yy_hold_char; -static int yy_n_chars; /* number of characters read into yy_ch_buf */ -int yyleng; - -/* Points to current character in buffer. */ -static char *yy_c_buf_p = (char *) 0; -static int yy_init = 1; /* whether we need to initialize */ -static int yy_start = 0; /* start state number */ - -/* Flag which is used to allow yywrap()'s to do buffer switches - * instead of setting up a fresh yyin. A bit of a hack ... - */ -static int yy_did_buffer_switch_on_eof; - -void yyrestart (FILE *input_file ); -void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ); -YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ); -void yy_delete_buffer (YY_BUFFER_STATE b ); -void yy_flush_buffer (YY_BUFFER_STATE b ); -void yypush_buffer_state (YY_BUFFER_STATE new_buffer ); -void yypop_buffer_state (void ); - -static void yyensure_buffer_stack (void ); -static void yy_load_buffer_state (void ); -static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file ); - -#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER ) - -YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ); -YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ); -YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len ); - -void *yyalloc (yy_size_t ); -void *yyrealloc (void *,yy_size_t ); -void yyfree (void * ); - -#define yy_new_buffer yy_create_buffer - -#define yy_set_interactive(is_interactive) \ - { \ - if ( ! YY_CURRENT_BUFFER ){ \ - yyensure_buffer_stack (); \ - YY_CURRENT_BUFFER_LVALUE = \ - yy_create_buffer(yyin,YY_BUF_SIZE ); \ - } \ - YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ - } - -#define yy_set_bol(at_bol) \ - { \ - if ( ! YY_CURRENT_BUFFER ){\ - yyensure_buffer_stack (); \ - YY_CURRENT_BUFFER_LVALUE = \ - yy_create_buffer(yyin,YY_BUF_SIZE ); \ - } \ - YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ - } - -#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) - -/* Begin user sect3 */ - -#define yywrap(n) 1 -#define YY_SKIP_YYWRAP - -typedef unsigned char YY_CHAR; - -FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; - -typedef int yy_state_type; - -extern int yylineno; - -int yylineno = 1; - -extern char *yytext; -#define yytext_ptr yytext - -static yy_state_type yy_get_previous_state (void ); -static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); -static int yy_get_next_buffer (void ); -static void yy_fatal_error (yyconst char msg[] ); - -/* Done after the current pattern has been matched and before the - * corresponding action - sets up yytext. - */ -#define YY_DO_BEFORE_ACTION \ - (yytext_ptr) = yy_bp; \ - yyleng = (size_t) (yy_cp - yy_bp); \ - (yy_hold_char) = *yy_cp; \ - *yy_cp = '\0'; \ - (yy_c_buf_p) = yy_cp; - -#define YY_NUM_RULES 29 -#define YY_END_OF_BUFFER 30 -/* This struct is not used in this scanner, - but its presence is necessary. */ -struct yy_trans_info - { - flex_int32_t yy_verify; - flex_int32_t yy_nxt; - }; -static yyconst flex_int16_t yy_accept[39] = - { 0, - 0, 0, 30, 28, 1, 1, 27, 23, 12, 6, - 7, 21, 24, 25, 22, 3, 4, 17, 28, 15, - 5, 11, 10, 26, 14, 9, 3, 0, 4, 19, - 18, 13, 16, 20, 5, 8, 2, 0 - } ; - -static yyconst flex_int32_t yy_ec[256] = - { 0, - 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 2, 4, 1, 1, 1, 5, 6, 1, 7, - 8, 9, 10, 1, 11, 1, 12, 13, 14, 14, - 14, 14, 14, 14, 14, 15, 15, 1, 1, 16, - 17, 18, 1, 1, 19, 19, 19, 19, 19, 19, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 1, 1, 1, 21, 20, 1, 19, 19, 19, 19, - - 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 22, - 20, 20, 1, 23, 1, 24, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1 - } ; - -static yyconst flex_int32_t yy_meta[25] = - { 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 2, 2, 2, 1, 1, 1, 2, 3, - 1, 3, 1, 1 - } ; - -static yyconst flex_int16_t yy_base[41] = - { 0, - 0, 0, 47, 48, 48, 48, 29, 48, 39, 48, - 48, 48, 48, 48, 48, 12, 14, 14, 27, 15, - 0, 48, 20, 48, 48, 48, 22, 0, 24, 48, - 48, 48, 48, 48, 0, 48, 0, 48, 38, 40 - } ; - -static yyconst flex_int16_t yy_def[41] = - { 0, - 38, 1, 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, - 39, 38, 38, 38, 38, 38, 38, 40, 38, 38, - 38, 38, 38, 38, 39, 38, 40, 0, 38, 38 - } ; - -static yyconst flex_int16_t yy_nxt[73] = - { 0, - 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, - 14, 15, 16, 17, 17, 18, 19, 20, 21, 21, - 22, 21, 23, 24, 27, 27, 29, 29, 29, 30, - 31, 33, 34, 28, 27, 27, 29, 29, 29, 35, - 35, 37, 36, 32, 26, 25, 38, 3, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38 - } ; - -static yyconst flex_int16_t yy_chk[73] = - { 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 16, 16, 17, 17, 17, 18, - 18, 20, 20, 16, 27, 27, 29, 29, 29, 39, - 39, 40, 23, 19, 9, 7, 3, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38 - } ; - -static yy_state_type yy_last_accepting_state; -static char *yy_last_accepting_cpos; - -extern int yy_flex_debug; -int yy_flex_debug = 0; - -/* The intent behind this definition is that it'll catch - * any uses of REJECT which flex missed. - */ -#define REJECT reject_used_but_not_detected -#define yymore() yymore_used_but_not_detected -#define YY_MORE_ADJ 0 -#define YY_RESTORE_YY_MORE_OFFSET -char *yytext; -#line 1 "arith_lex.l" -#line 2 "arith_lex.l" -/* $NetBSD: arith_lex.l,v 1.12.6.1 2005/04/07 11:38:58 tron Exp $ */ - -/*- - * Copyright (c) 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * 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[] = "@(#)arith_lex.l 8.3 (Berkeley) 5/4/95"; -#else -__RCSID("$NetBSD: arith_lex.l,v 1.12.6.1 2005/04/07 11:38:58 tron Exp $"); -#endif -#endif /* not lint */ - -#include <unistd.h> -#include "arith.h" -#include "error.h" -#include "expand.h" -#include "var.h" - -extern int yylval; -extern char *arith_buf, *arith_startbuf; -#undef YY_INPUT -#define YY_INPUT(buf,result,max) \ - result = (*buf = *arith_buf++) ? 1 : YY_NULL; -#define YY_NO_UNPUT -#line 526 "arith_lex.c" - -#define INITIAL 0 - -#ifndef YY_NO_UNISTD_H -/* Special case for "unistd.h", since it is non-ANSI. We include it way - * down here because we want the user's section 1 to have been scanned first. - * The user has a chance to override it with an option. - */ -#include <unistd.h> -#endif - -#ifndef YY_EXTRA_TYPE -#define YY_EXTRA_TYPE void * -#endif - -/* Macros after this point can all be overridden by user definitions in - * section 1. - */ - -#ifndef YY_SKIP_YYWRAP -#ifdef __cplusplus -extern "C" int yywrap (void ); -#else -extern int yywrap (void ); -#endif -#endif - - static void yyunput (int c,char *buf_ptr ); - -#ifndef yytext_ptr -static void yy_flex_strncpy (char *,yyconst char *,int ); -#endif - -#ifdef YY_NEED_STRLEN -static int yy_flex_strlen (yyconst char * ); -#endif - -#ifndef YY_NO_INPUT - -#ifdef __cplusplus -static int yyinput (void ); -#else -static int input (void ); -#endif - -#endif - -/* Amount of stuff to slurp up with each read. */ -#ifndef YY_READ_BUF_SIZE -#define YY_READ_BUF_SIZE 8192 -#endif - -/* Copy whatever the last rule matched to the standard output. */ -#ifndef ECHO -/* This used to be an fputs(), but since the string might contain NUL's, - * we now use fwrite(). - */ -#define ECHO (void) fwrite( yytext, yyleng, 1, yyout ) -#endif - -/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, - * is returned in "result". - */ -#ifndef YY_INPUT -#define YY_INPUT(buf,result,max_size) \ - if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ - { \ - int c = '*'; \ - size_t n; \ - for ( n = 0; n < max_size && \ - (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ - buf[n] = (char) c; \ - if ( c == '\n' ) \ - buf[n++] = (char) c; \ - if ( c == EOF && ferror( yyin ) ) \ - YY_FATAL_ERROR( "input in flex scanner failed" ); \ - result = n; \ - } \ - else \ - { \ - errno=0; \ - while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ - { \ - if( errno != EINTR) \ - { \ - YY_FATAL_ERROR( "input in flex scanner failed" ); \ - break; \ - } \ - errno=0; \ - clearerr(yyin); \ - } \ - }\ -\ - -#endif - -/* No semi-colon after return; correct usage is to write "yyterminate();" - - * we don't want an extra ';' after the "return" because that will cause - * some compilers to complain about unreachable statements. - */ -#ifndef yyterminate -#define yyterminate() return YY_NULL -#endif - -/* Number of entries by which start-condition stack grows. */ -#ifndef YY_START_STACK_INCR -#define YY_START_STACK_INCR 25 -#endif - -/* Report a fatal error. */ -#ifndef YY_FATAL_ERROR -#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) -#endif - -/* end tables serialization structures and prototypes */ - -/* Default declaration of generated scanner - a define so the user can - * easily add parameters. - */ -#ifndef YY_DECL -#define YY_DECL_IS_OURS 1 - -extern int yylex (void); - -#define YY_DECL int yylex (void) -#endif /* !YY_DECL */ - -/* Code executed at the beginning of each rule, after yytext and yyleng - * have been set up. - */ -#ifndef YY_USER_ACTION -#define YY_USER_ACTION -#endif - -/* Code executed at the end of each rule. */ -#ifndef YY_BREAK -#define YY_BREAK break; -#endif - -#define YY_RULE_SETUP \ - YY_USER_ACTION - -/** The main scanner function which does all the work. - */ -YY_DECL -{ - register yy_state_type yy_current_state; - register char *yy_cp, *yy_bp; - register int yy_act; - -#line 60 "arith_lex.l" - -#line 679 "arith_lex.c" - - if ( (yy_init) ) - { - (yy_init) = 0; - -#ifdef YY_USER_INIT - YY_USER_INIT; -#endif - - if ( ! (yy_start) ) - (yy_start) = 1; /* first start state */ - - if ( ! yyin ) - yyin = stdin; - - if ( ! yyout ) - yyout = stdout; - - if ( ! YY_CURRENT_BUFFER ) { - yyensure_buffer_stack (); - YY_CURRENT_BUFFER_LVALUE = - yy_create_buffer(yyin,YY_BUF_SIZE ); - } - - yy_load_buffer_state( ); - } - - while ( 1 ) /* loops until end-of-file is reached */ - { - yy_cp = (yy_c_buf_p); - - /* Support of yytext. */ - *yy_cp = (yy_hold_char); - - /* yy_bp points to the position in yy_ch_buf of the start of - * the current run. - */ - yy_bp = yy_cp; - - yy_current_state = (yy_start); -yy_match: - do - { - register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; - if ( yy_accept[yy_current_state] ) - { - (yy_last_accepting_state) = yy_current_state; - (yy_last_accepting_cpos) = yy_cp; - } - while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) - { - yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 39 ) - yy_c = yy_meta[(unsigned int) yy_c]; - } - yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; - ++yy_cp; - } - while ( yy_base[yy_current_state] != 48 ); - -yy_find_action: - yy_act = yy_accept[yy_current_state]; - if ( yy_act == 0 ) - { /* have to back up */ - yy_cp = (yy_last_accepting_cpos); - yy_current_state = (yy_last_accepting_state); - yy_act = yy_accept[yy_current_state]; - } - - YY_DO_BEFORE_ACTION; - -do_action: /* This label is used only to access EOF actions. */ - - switch ( yy_act ) - { /* beginning of action switch */ - case 0: /* must back up */ - /* undo the effects of YY_DO_BEFORE_ACTION */ - *yy_cp = (yy_hold_char); - yy_cp = (yy_last_accepting_cpos); - yy_current_state = (yy_last_accepting_state); - goto yy_find_action; - -case 1: -/* rule 1 can match eol */ -YY_RULE_SETUP -#line 61 "arith_lex.l" -{ ; } - YY_BREAK -case 2: -YY_RULE_SETUP -#line 62 "arith_lex.l" -{ yylval = strtol(yytext, 0, 0); return(ARITH_NUM); } - YY_BREAK -case 3: -YY_RULE_SETUP -#line 63 "arith_lex.l" -{ yylval = strtol(yytext, 0, 0); return(ARITH_NUM); } - YY_BREAK -case 4: -YY_RULE_SETUP -#line 64 "arith_lex.l" -{ yylval = strtol(yytext, 0, 0); return(ARITH_NUM); } - YY_BREAK -case 5: -YY_RULE_SETUP -#line 65 "arith_lex.l" -{ char *v = lookupvar(yytext); - if (v) { - yylval = strtol(v, &v, 0); - if (*v == 0) - return ARITH_NUM; - } - error("arith: syntax error: \"%s\"", arith_startbuf); - } - YY_BREAK -case 6: -YY_RULE_SETUP -#line 73 "arith_lex.l" -{ return(ARITH_LPAREN); } - YY_BREAK -case 7: -YY_RULE_SETUP -#line 74 "arith_lex.l" -{ return(ARITH_RPAREN); } - YY_BREAK -case 8: -YY_RULE_SETUP -#line 75 "arith_lex.l" -{ return(ARITH_OR); } - YY_BREAK -case 9: -YY_RULE_SETUP -#line 76 "arith_lex.l" -{ return(ARITH_AND); } - YY_BREAK -case 10: -YY_RULE_SETUP -#line 77 "arith_lex.l" -{ return(ARITH_BOR); } - YY_BREAK -case 11: -YY_RULE_SETUP -#line 78 "arith_lex.l" -{ return(ARITH_BXOR); } - YY_BREAK -case 12: -YY_RULE_SETUP -#line 79 "arith_lex.l" -{ return(ARITH_BAND); } - YY_BREAK -case 13: -YY_RULE_SETUP -#line 80 "arith_lex.l" -{ return(ARITH_EQ); } - YY_BREAK -case 14: -YY_RULE_SETUP -#line 81 "arith_lex.l" -{ return(ARITH_NE); } - YY_BREAK -case 15: -YY_RULE_SETUP -#line 82 "arith_lex.l" -{ return(ARITH_GT); } - YY_BREAK -case 16: -YY_RULE_SETUP -#line 83 "arith_lex.l" -{ return(ARITH_GE); } - YY_BREAK -case 17: -YY_RULE_SETUP -#line 84 "arith_lex.l" -{ return(ARITH_LT); } - YY_BREAK -case 18: -YY_RULE_SETUP -#line 85 "arith_lex.l" -{ return(ARITH_LE); } - YY_BREAK -case 19: -YY_RULE_SETUP -#line 86 "arith_lex.l" -{ return(ARITH_LSHIFT); } - YY_BREAK -case 20: -YY_RULE_SETUP -#line 87 "arith_lex.l" -{ return(ARITH_RSHIFT); } - YY_BREAK -case 21: -YY_RULE_SETUP -#line 88 "arith_lex.l" -{ return(ARITH_MUL); } - YY_BREAK -case 22: -YY_RULE_SETUP -#line 89 "arith_lex.l" -{ return(ARITH_DIV); } - YY_BREAK -case 23: -YY_RULE_SETUP -#line 90 "arith_lex.l" -{ return(ARITH_REM); } - YY_BREAK -case 24: -YY_RULE_SETUP -#line 91 "arith_lex.l" -{ return(ARITH_ADD); } - YY_BREAK -case 25: -YY_RULE_SETUP -#line 92 "arith_lex.l" -{ return(ARITH_SUB); } - YY_BREAK -case 26: -YY_RULE_SETUP -#line 93 "arith_lex.l" -{ return(ARITH_BNOT); } - YY_BREAK -case 27: -YY_RULE_SETUP -#line 94 "arith_lex.l" -{ return(ARITH_NOT); } - YY_BREAK -case 28: -YY_RULE_SETUP -#line 95 "arith_lex.l" -{ error("arith: syntax error: \"%s\"", arith_startbuf); } - YY_BREAK -case 29: -YY_RULE_SETUP -#line 96 "arith_lex.l" -ECHO; - YY_BREAK -#line 915 "arith_lex.c" -case YY_STATE_EOF(INITIAL): - yyterminate(); - - case YY_END_OF_BUFFER: - { - /* Amount of text matched not including the EOB char. */ - int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; - - /* Undo the effects of YY_DO_BEFORE_ACTION. */ - *yy_cp = (yy_hold_char); - YY_RESTORE_YY_MORE_OFFSET - - if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) - { - /* We're scanning a new file or input source. It's - * possible that this happened because the user - * just pointed yyin at a new source and called - * yylex(). If so, then we have to assure - * consistency between YY_CURRENT_BUFFER and our - * globals. Here is the right place to do so, because - * this is the first action (other than possibly a - * back-up) that will match for the new input source. - */ - (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; - YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; - YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; - } - - /* Note that here we test for yy_c_buf_p "<=" to the position - * of the first EOB in the buffer, since yy_c_buf_p will - * already have been incremented past the NUL character - * (since all states make transitions on EOB to the - * end-of-buffer state). Contrast this with the test - * in input(). - */ - if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) - { /* This was really a NUL. */ - yy_state_type yy_next_state; - - (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; - - yy_current_state = yy_get_previous_state( ); - - /* Okay, we're now positioned to make the NUL - * transition. We couldn't have - * yy_get_previous_state() go ahead and do it - * for us because it doesn't know how to deal - * with the possibility of jamming (and we don't - * want to build jamming into it because then it - * will run more slowly). - */ - - yy_next_state = yy_try_NUL_trans( yy_current_state ); - - yy_bp = (yytext_ptr) + YY_MORE_ADJ; - - if ( yy_next_state ) - { - /* Consume the NUL. */ - yy_cp = ++(yy_c_buf_p); - yy_current_state = yy_next_state; - goto yy_match; - } - - else - { - yy_cp = (yy_c_buf_p); - goto yy_find_action; - } - } - - else switch ( yy_get_next_buffer( ) ) - { - case EOB_ACT_END_OF_FILE: - { - (yy_did_buffer_switch_on_eof) = 0; - - if ( yywrap( ) ) - { - /* Note: because we've taken care in - * yy_get_next_buffer() to have set up - * yytext, we can now set up - * yy_c_buf_p so that if some total - * hoser (like flex itself) wants to - * call the scanner after we return the - * YY_NULL, it'll still work - another - * YY_NULL will get returned. - */ - (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; - - yy_act = YY_STATE_EOF(YY_START); - goto do_action; - } - - else - { - if ( ! (yy_did_buffer_switch_on_eof) ) - YY_NEW_FILE; - } - break; - } - - case EOB_ACT_CONTINUE_SCAN: - (yy_c_buf_p) = - (yytext_ptr) + yy_amount_of_matched_text; - - yy_current_state = yy_get_previous_state( ); - - yy_cp = (yy_c_buf_p); - yy_bp = (yytext_ptr) + YY_MORE_ADJ; - goto yy_match; - - case EOB_ACT_LAST_MATCH: - (yy_c_buf_p) = - &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; - - yy_current_state = yy_get_previous_state( ); - - yy_cp = (yy_c_buf_p); - yy_bp = (yytext_ptr) + YY_MORE_ADJ; - goto yy_find_action; - } - break; - } - - default: - YY_FATAL_ERROR( - "fatal flex scanner internal error--no action found" ); - } /* end of action switch */ - } /* end of scanning one token */ -} /* end of yylex */ - -/* yy_get_next_buffer - try to read in a new buffer - * - * Returns a code representing an action: - * EOB_ACT_LAST_MATCH - - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position - * EOB_ACT_END_OF_FILE - end of file - */ -static int yy_get_next_buffer (void) -{ - register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; - register char *source = (yytext_ptr); - register int number_to_move, i; - int ret_val; - - if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) - YY_FATAL_ERROR( - "fatal flex scanner internal error--end of buffer missed" ); - - if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) - { /* Don't try to fill the buffer, so this is an EOF. */ - if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) - { - /* We matched a single character, the EOB, so - * treat this as a final EOF. - */ - return EOB_ACT_END_OF_FILE; - } - - else - { - /* We matched some text prior to the EOB, first - * process it. - */ - return EOB_ACT_LAST_MATCH; - } - } - - /* Try to read more data. */ - - /* First move last chars to start of buffer. */ - number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; - - for ( i = 0; i < number_to_move; ++i ) - *(dest++) = *(source++); - - if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) - /* don't do the read, it's not guaranteed to return an EOF, - * just force an EOF - */ - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; - - else - { - size_t num_to_read = - YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; - - while ( num_to_read <= 0 ) - { /* Not enough room in the buffer - grow it. */ - - /* just a shorter name for the current buffer */ - YY_BUFFER_STATE b = YY_CURRENT_BUFFER; - - int yy_c_buf_p_offset = - (int) ((yy_c_buf_p) - b->yy_ch_buf); - - if ( b->yy_is_our_buffer ) - { - int new_size = b->yy_buf_size * 2; - - if ( new_size <= 0 ) - b->yy_buf_size += b->yy_buf_size / 8; - else - b->yy_buf_size *= 2; - - b->yy_ch_buf = (char *) - /* Include room in for 2 EOB chars. */ - yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); - } - else - /* Can't grow it, we don't own it. */ - b->yy_ch_buf = 0; - - if ( ! b->yy_ch_buf ) - YY_FATAL_ERROR( - "fatal error - scanner input buffer overflow" ); - - (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; - - num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - - number_to_move - 1; - - } - - if ( num_to_read > YY_READ_BUF_SIZE ) - num_to_read = YY_READ_BUF_SIZE; - - /* Read in more data. */ - YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), - (yy_n_chars), num_to_read ); - - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); - } - - if ( (yy_n_chars) == 0 ) - { - if ( number_to_move == YY_MORE_ADJ ) - { - ret_val = EOB_ACT_END_OF_FILE; - yyrestart(yyin ); - } - - else - { - ret_val = EOB_ACT_LAST_MATCH; - YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = - YY_BUFFER_EOF_PENDING; - } - } - - else - ret_val = EOB_ACT_CONTINUE_SCAN; - - (yy_n_chars) += number_to_move; - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; - - (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; - - return ret_val; -} - -/* yy_get_previous_state - get the state just before the EOB char was reached */ - - static yy_state_type yy_get_previous_state (void) -{ - register yy_state_type yy_current_state; - register char *yy_cp; - - yy_current_state = (yy_start); - - for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) - { - register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); - if ( yy_accept[yy_current_state] ) - { - (yy_last_accepting_state) = yy_current_state; - (yy_last_accepting_cpos) = yy_cp; - } - while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) - { - yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 39 ) - yy_c = yy_meta[(unsigned int) yy_c]; - } - yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; - } - - return yy_current_state; -} - -/* yy_try_NUL_trans - try to make a transition on the NUL character - * - * synopsis - * next_state = yy_try_NUL_trans( current_state ); - */ - static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) -{ - register int yy_is_jam; - register char *yy_cp = (yy_c_buf_p); - - register YY_CHAR yy_c = 1; - if ( yy_accept[yy_current_state] ) - { - (yy_last_accepting_state) = yy_current_state; - (yy_last_accepting_cpos) = yy_cp; - } - while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) - { - yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 39 ) - yy_c = yy_meta[(unsigned int) yy_c]; - } - yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; - yy_is_jam = (yy_current_state == 38); - - return yy_is_jam ? 0 : yy_current_state; -} - - static void yyunput (int c, register char * yy_bp ) -{ - register char *yy_cp; - - yy_cp = (yy_c_buf_p); - - /* undo effects of setting up yytext */ - *yy_cp = (yy_hold_char); - - if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) - { /* need to shift things up to make room */ - /* +2 for EOB chars. */ - register int number_to_move = (yy_n_chars) + 2; - register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ - YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; - register char *source = - &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; - - while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) - *--dest = *--source; - - yy_cp += (int) (dest - source); - yy_bp += (int) (dest - source); - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = - (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size; - - if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) - YY_FATAL_ERROR( "flex scanner push-back overflow" ); - } - - *--yy_cp = (char) c; - - (yytext_ptr) = yy_bp; - (yy_hold_char) = *yy_cp; - (yy_c_buf_p) = yy_cp; -} - -#ifndef YY_NO_INPUT -#ifdef __cplusplus - static int yyinput (void) -#else - static int input (void) -#endif - -{ - int c; - - *(yy_c_buf_p) = (yy_hold_char); - - if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) - { - /* yy_c_buf_p now points to the character we want to return. - * If this occurs *before* the EOB characters, then it's a - * valid NUL; if not, then we've hit the end of the buffer. - */ - if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) - /* This was really a NUL. */ - *(yy_c_buf_p) = '\0'; - - else - { /* need more input */ - int offset = (yy_c_buf_p) - (yytext_ptr); - ++(yy_c_buf_p); - - switch ( yy_get_next_buffer( ) ) - { - case EOB_ACT_LAST_MATCH: - /* This happens because yy_g_n_b() - * sees that we've accumulated a - * token and flags that we need to - * try matching the token before - * proceeding. But for input(), - * there's no matching to consider. - * So convert the EOB_ACT_LAST_MATCH - * to EOB_ACT_END_OF_FILE. - */ - - /* Reset buffer status. */ - yyrestart(yyin ); - - /*FALLTHROUGH*/ - - case EOB_ACT_END_OF_FILE: - { - if ( yywrap( ) ) - return EOF; - - if ( ! (yy_did_buffer_switch_on_eof) ) - YY_NEW_FILE; -#ifdef __cplusplus - return yyinput(); -#else - return input(); -#endif - } - - case EOB_ACT_CONTINUE_SCAN: - (yy_c_buf_p) = (yytext_ptr) + offset; - break; - } - } - } - - c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ - *(yy_c_buf_p) = '\0'; /* preserve yytext */ - (yy_hold_char) = *++(yy_c_buf_p); - - return c; -} -#endif /* ifndef YY_NO_INPUT */ - -/** Immediately switch to a different input stream. - * @param input_file A readable stream. - * - * @note This function does not reset the start condition to @c INITIAL . - */ - void yyrestart (FILE * input_file ) -{ - - if ( ! YY_CURRENT_BUFFER ){ - yyensure_buffer_stack (); - YY_CURRENT_BUFFER_LVALUE = - yy_create_buffer(yyin,YY_BUF_SIZE ); - } - - yy_init_buffer(YY_CURRENT_BUFFER,input_file ); - yy_load_buffer_state( ); -} - -/** Switch to a different input buffer. - * @param new_buffer The new input buffer. - * - */ - void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) -{ - - /* TODO. We should be able to replace this entire function body - * with - * yypop_buffer_state(); - * yypush_buffer_state(new_buffer); - */ - yyensure_buffer_stack (); - if ( YY_CURRENT_BUFFER == new_buffer ) - return; - - if ( YY_CURRENT_BUFFER ) - { - /* Flush out information for old buffer. */ - *(yy_c_buf_p) = (yy_hold_char); - YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); - } - - YY_CURRENT_BUFFER_LVALUE = new_buffer; - yy_load_buffer_state( ); - - /* We don't actually know whether we did this switch during - * EOF (yywrap()) processing, but the only time this flag - * is looked at is after yywrap() is called, so it's safe - * to go ahead and always set it. - */ - (yy_did_buffer_switch_on_eof) = 1; -} - -static void yy_load_buffer_state (void) -{ - (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; - (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; - yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; - (yy_hold_char) = *(yy_c_buf_p); -} - -/** Allocate and initialize an input buffer state. - * @param file A readable stream. - * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. - * - * @return the allocated buffer state. - */ - YY_BUFFER_STATE yy_create_buffer (FILE * file, int size ) -{ - YY_BUFFER_STATE b; - - b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ); - if ( ! b ) - YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); - - b->yy_buf_size = size; - - /* yy_ch_buf has to be 2 characters longer than the size given because - * we need to put in 2 end-of-buffer characters. - */ - b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2 ); - if ( ! b->yy_ch_buf ) - YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); - - b->yy_is_our_buffer = 1; - - yy_init_buffer(b,file ); - - return b; -} - -/** Destroy the buffer. - * @param b a buffer created with yy_create_buffer() - * - */ - void yy_delete_buffer (YY_BUFFER_STATE b ) -{ - - if ( ! b ) - return; - - if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ - YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; - - if ( b->yy_is_our_buffer ) - yyfree((void *) b->yy_ch_buf ); - - yyfree((void *) b ); -} - -#ifndef __cplusplus -extern int isatty (int ); -#endif /* __cplusplus */ - -/* Initializes or reinitializes a buffer. - * This function is sometimes called more than once on the same buffer, - * such as during a yyrestart() or at EOF. - */ - static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file ) - -{ - int oerrno = errno; - - yy_flush_buffer(b ); - - b->yy_input_file = file; - b->yy_fill_buffer = 1; - - /* If b is the current buffer, then yy_init_buffer was _probably_ - * called from yyrestart() or through yy_get_next_buffer. - * In that case, we don't want to reset the lineno or column. - */ - if (b != YY_CURRENT_BUFFER){ - b->yy_bs_lineno = 1; - b->yy_bs_column = 0; - } - - b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; - - errno = oerrno; -} - -/** Discard all buffered characters. On the next scan, YY_INPUT will be called. - * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. - * - */ - void yy_flush_buffer (YY_BUFFER_STATE b ) -{ - if ( ! b ) - return; - - b->yy_n_chars = 0; - - /* We always need two end-of-buffer characters. The first causes - * a transition to the end-of-buffer state. The second causes - * a jam in that state. - */ - b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; - b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; - - b->yy_buf_pos = &b->yy_ch_buf[0]; - - b->yy_at_bol = 1; - b->yy_buffer_status = YY_BUFFER_NEW; - - if ( b == YY_CURRENT_BUFFER ) - yy_load_buffer_state( ); -} - -/** Pushes the new state onto the stack. The new state becomes - * the current state. This function will allocate the stack - * if necessary. - * @param new_buffer The new state. - * - */ -void yypush_buffer_state (YY_BUFFER_STATE new_buffer ) -{ - if (new_buffer == NULL) - return; - - yyensure_buffer_stack(); - - /* This block is copied from yy_switch_to_buffer. */ - if ( YY_CURRENT_BUFFER ) - { - /* Flush out information for old buffer. */ - *(yy_c_buf_p) = (yy_hold_char); - YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); - } - - /* Only push if top exists. Otherwise, replace top. */ - if (YY_CURRENT_BUFFER) - (yy_buffer_stack_top)++; - YY_CURRENT_BUFFER_LVALUE = new_buffer; - - /* copied from yy_switch_to_buffer. */ - yy_load_buffer_state( ); - (yy_did_buffer_switch_on_eof) = 1; -} - -/** Removes and deletes the top of the stack, if present. - * The next element becomes the new top. - * - */ -void yypop_buffer_state (void) -{ - if (!YY_CURRENT_BUFFER) - return; - - yy_delete_buffer(YY_CURRENT_BUFFER ); - YY_CURRENT_BUFFER_LVALUE = NULL; - if ((yy_buffer_stack_top) > 0) - --(yy_buffer_stack_top); - - if (YY_CURRENT_BUFFER) { - yy_load_buffer_state( ); - (yy_did_buffer_switch_on_eof) = 1; - } -} - -/* Allocates the stack if it does not exist. - * Guarantees space for at least one push. - */ -static void yyensure_buffer_stack (void) -{ - int num_to_alloc; - - if (!(yy_buffer_stack)) { - - /* First allocation is just for 2 elements, since we don't know if this - * scanner will even need a stack. We use 2 instead of 1 to avoid an - * immediate realloc on the next call. - */ - num_to_alloc = 1; - (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc - (num_to_alloc * sizeof(struct yy_buffer_state*) - ); - - memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); - - (yy_buffer_stack_max) = num_to_alloc; - (yy_buffer_stack_top) = 0; - return; - } - - if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ - - /* Increase the buffer to prepare for a possible push. */ - int grow_size = 8 /* arbitrary grow size */; - - num_to_alloc = (yy_buffer_stack_max) + grow_size; - (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc - ((yy_buffer_stack), - num_to_alloc * sizeof(struct yy_buffer_state*) - ); - - /* zero only the new slots.*/ - memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); - (yy_buffer_stack_max) = num_to_alloc; - } -} - -/** Setup the input buffer state to scan directly from a user-specified character buffer. - * @param base the character buffer - * @param size the size in bytes of the character buffer - * - * @return the newly allocated buffer state object. - */ -YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size ) -{ - YY_BUFFER_STATE b; - - if ( size < 2 || - base[size-2] != YY_END_OF_BUFFER_CHAR || - base[size-1] != YY_END_OF_BUFFER_CHAR ) - /* They forgot to leave room for the EOB's. */ - return 0; - - b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ); - if ( ! b ) - YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); - - b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ - b->yy_buf_pos = b->yy_ch_buf = base; - b->yy_is_our_buffer = 0; - b->yy_input_file = 0; - b->yy_n_chars = b->yy_buf_size; - b->yy_is_interactive = 0; - b->yy_at_bol = 1; - b->yy_fill_buffer = 0; - b->yy_buffer_status = YY_BUFFER_NEW; - - yy_switch_to_buffer(b ); - - return b; -} - -/** Setup the input buffer state to scan a string. The next call to yylex() will - * scan from a @e copy of @a str. - * @param str a NUL-terminated string to scan - * - * @return the newly allocated buffer state object. - * @note If you want to scan bytes that may contain NUL values, then use - * yy_scan_bytes() instead. - */ -YY_BUFFER_STATE yy_scan_string (yyconst char * yy_str ) -{ - - return yy_scan_bytes(yy_str,strlen(yy_str) ); -} - -/** Setup the input buffer state to scan the given bytes. The next call to yylex() will - * scan from a @e copy of @a bytes. - * @param bytes the byte buffer to scan - * @param len the number of bytes in the buffer pointed to by @a bytes. - * - * @return the newly allocated buffer state object. - */ -YY_BUFFER_STATE yy_scan_bytes (yyconst char * bytes, int len ) -{ - YY_BUFFER_STATE b; - char *buf; - yy_size_t n; - int i; - - /* Get memory for full buffer, including space for trailing EOB's. */ - n = len + 2; - buf = (char *) yyalloc(n ); - if ( ! buf ) - YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); - - for ( i = 0; i < len; ++i ) - buf[i] = bytes[i]; - - buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR; - - b = yy_scan_buffer(buf,n ); - if ( ! b ) - YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); - - /* It's okay to grow etc. this buffer, and we should throw it - * away when we're done. - */ - b->yy_is_our_buffer = 1; - - return b; -} - -#ifndef YY_EXIT_FAILURE -#define YY_EXIT_FAILURE 2 -#endif - -static void yy_fatal_error (yyconst char* msg ) -{ - (void) fprintf( stderr, "%s\n", msg ); - exit( YY_EXIT_FAILURE ); -} - -/* Redefine yyless() so it works in section 3 code. */ - -#undef yyless -#define yyless(n) \ - do \ - { \ - /* Undo effects of setting up yytext. */ \ - int yyless_macro_arg = (n); \ - YY_LESS_LINENO(yyless_macro_arg);\ - yytext[yyleng] = (yy_hold_char); \ - (yy_c_buf_p) = yytext + yyless_macro_arg; \ - (yy_hold_char) = *(yy_c_buf_p); \ - *(yy_c_buf_p) = '\0'; \ - yyleng = yyless_macro_arg; \ - } \ - while ( 0 ) - -/* Accessor methods (get/set functions) to struct members. */ - -/** Get the current line number. - * - */ -int yyget_lineno (void) -{ - - return yylineno; -} - -/** Get the input stream. - * - */ -FILE *yyget_in (void) -{ - return yyin; -} - -/** Get the output stream. - * - */ -FILE *yyget_out (void) -{ - return yyout; -} - -/** Get the length of the current token. - * - */ -int yyget_leng (void) -{ - return yyleng; -} - -/** Get the current token. - * - */ - -char *yyget_text (void) -{ - return yytext; -} - -/** Set the current line number. - * @param line_number - * - */ -void yyset_lineno (int line_number ) -{ - - yylineno = line_number; -} - -/** Set the input stream. This does not discard the current - * input buffer. - * @param in_str A readable stream. - * - * @see yy_switch_to_buffer - */ -void yyset_in (FILE * in_str ) -{ - yyin = in_str ; -} - -void yyset_out (FILE * out_str ) -{ - yyout = out_str ; -} - -int yyget_debug (void) -{ - return yy_flex_debug; -} - -void yyset_debug (int bdebug ) -{ - yy_flex_debug = bdebug ; -} - -/* yylex_destroy is for both reentrant and non-reentrant scanners. */ -int yylex_destroy (void) -{ - - /* Pop the buffer stack, destroying each element. */ - while(YY_CURRENT_BUFFER){ - yy_delete_buffer(YY_CURRENT_BUFFER ); - YY_CURRENT_BUFFER_LVALUE = NULL; - yypop_buffer_state(); - } - - /* Destroy the stack itself. */ - yyfree((yy_buffer_stack) ); - (yy_buffer_stack) = NULL; - - return 0; -} - -/* - * Internal utility routines. - */ - -#ifndef yytext_ptr -static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) -{ - register int i; - for ( i = 0; i < n; ++i ) - s1[i] = s2[i]; -} -#endif - -#ifdef YY_NEED_STRLEN -static int yy_flex_strlen (yyconst char * s ) -{ - register int n; - for ( n = 0; s[n]; ++n ) - ; - - return n; -} -#endif - -void *yyalloc (yy_size_t size ) -{ - return (void *) malloc( size ); -} - -void *yyrealloc (void * ptr, yy_size_t size ) -{ - /* The cast to (char *) in the following accommodates both - * implementations that use char* generic pointers, and those - * that use void* generic pointers. It works with the latter - * because both ANSI C and C++ allow castless assignment from - * any pointer type to void*, and deal with argument conversions - * as though doing an assignment. - */ - return (void *) realloc( (char *) ptr, size ); -} - -void yyfree (void * ptr ) -{ - free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ -} - -#define YYTABLES_NAME "yytables" - -#undef YY_NEW_FILE -#undef YY_FLUSH_BUFFER -#undef yy_set_bol -#undef yy_new_buffer -#undef yy_set_interactive -#undef yytext_ptr -#undef YY_DO_BEFORE_ACTION - -#ifdef YY_DECL_IS_OURS -#undef YY_DECL_IS_OURS -#undef YY_DECL -#endif -#line 96 "arith_lex.l" - - - -void -arith_lex_reset() { -#ifdef YY_NEW_FILE - YY_NEW_FILE; -#endif -} - diff --git a/sh/arith_lex.l b/sh/arith_lex.l deleted file mode 100644 index 79116fc..0000000 --- a/sh/arith_lex.l +++ /dev/null @@ -1,103 +0,0 @@ -%{ -/* $NetBSD: arith_lex.l,v 1.12.6.1 2005/04/07 11:38:58 tron Exp $ */ - -/*- - * Copyright (c) 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * 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[] = "@(#)arith_lex.l 8.3 (Berkeley) 5/4/95"; -#else -__RCSID("$NetBSD: arith_lex.l,v 1.12.6.1 2005/04/07 11:38:58 tron Exp $"); -#endif -#endif /* not lint */ - -#include <unistd.h> -#include "arith.h" -#include "error.h" -#include "expand.h" -#include "var.h" - -extern int yylval; -extern char *arith_buf, *arith_startbuf; -#undef YY_INPUT -#define YY_INPUT(buf,result,max) \ - result = (*buf = *arith_buf++) ? 1 : YY_NULL; -#define YY_NO_UNPUT -%} -%option noyywrap - -%% -[ \t\n] { ; } -0x[0-9a-fA-F]+ { yylval = strtol(yytext, 0, 0); return(ARITH_NUM); } -0[0-7]* { yylval = strtol(yytext, 0, 0); return(ARITH_NUM); } -[1-9][0-9]* { yylval = strtol(yytext, 0, 0); return(ARITH_NUM); } -[A-Za-z_][A-Za-z_0-9]* { char *v = lookupvar(yytext); - if (v) { - yylval = strtol(v, &v, 0); - if (*v == 0) - return ARITH_NUM; - } - error("arith: syntax error: \"%s\"", arith_startbuf); - } -"(" { return(ARITH_LPAREN); } -")" { return(ARITH_RPAREN); } -"||" { return(ARITH_OR); } -"&&" { return(ARITH_AND); } -"|" { return(ARITH_BOR); } -"^" { return(ARITH_BXOR); } -"&" { return(ARITH_BAND); } -"==" { return(ARITH_EQ); } -"!=" { return(ARITH_NE); } -">" { return(ARITH_GT); } -">=" { return(ARITH_GE); } -"<" { return(ARITH_LT); } -"<=" { return(ARITH_LE); } -"<<" { return(ARITH_LSHIFT); } -">>" { return(ARITH_RSHIFT); } -"*" { return(ARITH_MUL); } -"/" { return(ARITH_DIV); } -"%" { return(ARITH_REM); } -"+" { return(ARITH_ADD); } -"-" { return(ARITH_SUB); } -"~" { return(ARITH_BNOT); } -"!" { return(ARITH_NOT); } -. { error("arith: syntax error: \"%s\"", arith_startbuf); } -%% - -void -arith_lex_reset() { -#ifdef YY_NEW_FILE - YY_NEW_FILE; -#endif -} diff --git a/sh/bltin/bltin.h b/sh/bltin/bltin.h deleted file mode 100644 index b8f9d75..0000000 --- a/sh/bltin/bltin.h +++ /dev/null @@ -1,94 +0,0 @@ -/* $NetBSD: bltin.h,v 1.11 2003/08/07 09:05:40 agc 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 - * Kenneth Almquist. - * - * 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. - * - * @(#)bltin.h 8.1 (Berkeley) 5/31/93 - */ - -/* - * This file is included by programs which are optionally built into the - * shell. If SHELL is defined, we try to map the standard UNIX library - * routines to ash routines using defines. - */ - -#include "../shell.h" -#include "../mystring.h" -#ifdef SHELL -#include "../output.h" -#include "../error.h" -#undef stdout -#undef stderr -#undef putc -#undef putchar -#undef fileno -#define stdout out1 -#define stderr out2 -#define printf out1fmt -#define putc(c, file) outc(c, file) -#define putchar(c) out1c(c) -#define FILE struct output -#define fprintf outfmt -#define fputs outstr -#define fflush flushout -#define fileno(f) ((f)->fd) -#define INITARGS(argv) -#define err sh_err -#define verr sh_verr -#define errx sh_errx -#define verrx sh_verrx -#define warn sh_warn -#define vwarn sh_vwarn -#define warnx sh_warnx -#define vwarnx sh_vwarnx -#define exit sh_exit -#define setprogname(s) -#define getprogname() commandname -#define setlocate(l,s) 0 - -#define getenv(p) bltinlookup((p),0) - -#else -#undef NULL -#include <stdio.h> -#undef main -#define INITARGS(argv) if ((commandname = argv[0]) == NULL) {fputs("Argc is zero\n", stderr); exit(2);} else -#endif - -pointer stalloc(int); -void error(const char *, ...); -void sh_warnx(const char *, ...); -void sh_exit(int) __attribute__((__noreturn__)); - -int echocmd(int, char **); - - -extern const char *commandname; diff --git a/sh/bltin/echo.1 b/sh/bltin/echo.1 deleted file mode 100644 index 7e71fa3..0000000 --- a/sh/bltin/echo.1 +++ /dev/null @@ -1,109 +0,0 @@ -.\" $NetBSD: echo.1,v 1.13 2003/08/07 09:05:40 agc 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 -.\" Kenneth Almquist. -.\" Copyright 1989 by Kenneth Almquist -.\" -.\" 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. -.\" -.\" @(#)echo.1 8.1 (Berkeley) 5/31/93 -.\" -.Dd May 31, 1993 -.Dt ECHO 1 -.Os -.Sh NAME -.Nm echo -.Nd produce message in a shell script -.Sh SYNOPSIS -.Nm -.Op Fl n | Fl e -.Ar args ... -.Sh DESCRIPTION -.Nm -prints its arguments on the standard output, separated by spaces. -Unless the -.Fl n -option is present, a newline is output following the arguments. -The -.Fl e -option causes -.Nm -to treat the escape sequences specially, as described in the following -paragraph. -The -.Fl e -option is the default, and is provided solely for compatibility with -other systems. -Only one of the options -.Fl n -and -.Fl e -may be given. -.Pp -If any of the following sequences of characters is encountered during -output, the sequence is not output. Instead, the specified action is -performed: -.Bl -tag -width indent -.It Li \eb -A backspace character is output. -.It Li \ec -Subsequent output is suppressed. This is normally used at the end of the -last argument to suppress the trailing newline that -.Nm -would otherwise output. -.It Li \ef -Output a form feed. -.It Li \en -Output a newline character. -.It Li \er -Output a carriage return. -.It Li \et -Output a (horizontal) tab character. -.It Li \ev -Output a vertical tab. -.It Li \e0 Ns Ar digits -Output the character whose value is given by zero to three digits. -If there are zero digits, a nul character is output. -.It Li \e\e -Output a backslash. -.El -.Sh HINTS -Remember that backslash is special to the shell and needs to be escaped. -To output a message to standard error, say -.Pp -.D1 echo message \*[Gt]\*[Am]2 -.Sh BUGS -The octal character escape mechanism -.Pq Li \e0 Ns Ar digits -differs from the -C language mechanism. -.Pp -There is no way to force -.Nm -to treat its arguments literally, rather than interpreting them as -options and escape sequences. diff --git a/sh/bltin/echo.c b/sh/bltin/echo.c deleted file mode 100644 index bed7535..0000000 --- a/sh/bltin/echo.c +++ /dev/null @@ -1,116 +0,0 @@ -/* $NetBSD: echo.c,v 1.12 2005/02/06 04:43:43 perry 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 - * Kenneth Almquist. - * - * 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. - * - * @(#)echo.c 8.1 (Berkeley) 5/31/93 - */ - -/* - * Echo command. - * - * echo is steeped in tradition - several of them! - * netbsd has supported 'echo [-n | -e] args' in spite of -e not being - * documented anywhere. - * Posix requires that -n be supported, output from strings containing - * \ is implementation defined - * The Single Unix Spec requires that \ escapes be treated as if -e - * were set, but that -n not be treated as an option. - * (ksh supports 'echo [-eEn] args', but not -- so that it is actually - * impossible to actually output '-n') - * - * It is suggested that 'printf "%b" "string"' be used to get \ sequences - * expanded. printf is now a builtin of netbsd's sh and csh. - */ - -//#define main echocmd - -#include "bltin.h" - -int -echocmd(int argc, char **argv) -{ - char **ap; - char *p; - char c; - int count; - int nflag = 0; - int eflag = 0; - - ap = argv; - if (argc) - ap++; - - if ((p = *ap) != NULL) { - if (equal(p, "-n")) { - nflag = 1; - ap++; - } else if (equal(p, "-e")) { - eflag = 1; - ap++; - } - } - - while ((p = *ap++) != NULL) { - while ((c = *p++) != '\0') { - if (c == '\\' && eflag) { - switch (*p++) { - case 'a': c = '\a'; break; /* bell */ - case 'b': c = '\b'; break; - case 'c': return 0; /* exit */ - case 'e': c = 033; break; /* escape */ - case 'f': c = '\f'; break; - case 'n': c = '\n'; break; - case 'r': c = '\r'; break; - case 't': c = '\t'; break; - case 'v': c = '\v'; break; - case '\\': break; /* c = '\\' */ - case '0': - c = 0; - count = 3; - while (--count >= 0 && (unsigned)(*p - '0') < 8) - c = (c << 3) + (*p++ - '0'); - break; - default: - /* Output the '/' and char following */ - p--; - break; - } - } - putchar(c); - } - if (*ap) - putchar(' '); - } - if (! nflag) - putchar('\n'); - return 0; -} diff --git a/sh/builtins.c b/sh/builtins.c deleted file mode 100644 index 344dbd6..0000000 --- a/sh/builtins.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * This file was generated by the mkbuiltins program. - */ - -#include "shell.h" -#include "builtins.h" - -const struct builtincmd builtincmd[] = { - - { "command", bltincmd }, - { "bg", bgcmd }, - { "cd", cdcmd }, - { "chdir", cdcmd }, - { "echo", echocmd }, - { "exp", expcmd }, - { "let", expcmd }, - { "false", falsecmd }, -#if WITH_HISTORY - { "fc", histcmd }, - { "inputrc", inputrc }, -#endif - { "fg", fgcmd }, - { "getopts", getoptscmd }, - { "hash", hashcmd }, - { "jobid", jobidcmd }, - { "jobs", jobscmd }, - { "local", localcmd }, -#ifndef SMALL -#endif - { "pwd", pwdcmd }, - { "read", readcmd }, - { "setvar", setvarcmd }, - { "true", truecmd }, - { "type", typecmd }, - { "umask", umaskcmd }, - { "unalias", unaliascmd }, - { "wait", waitcmd }, - { "alias", aliascmd }, - { "ulimit", ulimitcmd }, - { "wordexp", wordexpcmd }, - { 0, 0 }, -}; - -const struct builtincmd splbltincmd[] = { - { "break", breakcmd }, - { "continue", breakcmd }, - { ".", dotcmd }, - { "eval", evalcmd }, - { "exec", execcmd }, - { "exit", exitcmd }, - { "export", exportcmd }, - { "readonly", exportcmd }, - { "return", returncmd }, - { "set", setcmd }, - { "shift", shiftcmd }, - { "times", timescmd }, - { "trap", trapcmd }, - { ":", truecmd }, - { "unset", unsetcmd }, - { 0, 0 }, -}; diff --git a/sh/builtins.def b/sh/builtins.def deleted file mode 100644 index 18e56c6..0000000 --- a/sh/builtins.def +++ /dev/null @@ -1,94 +0,0 @@ -#!/bin/sh - -# $NetBSD: builtins.def,v 1.21 2004/07/13 15:05:59 seb 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 -# Kenneth Almquist. -# -# 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. -# -# @(#)builtins.def 8.4 (Berkeley) 5/4/95 - -# -# This file lists all the builtin commands. The first column is the name -# of a C routine. -# The -j flag specifies that this command is to be excluded from systems -# without job control. -# The -h flag specifies that this command is to be excluded from systems -# based on the SMALL compile-time symbol. -# The -s flag specifies that this is a posix 'special builtin' command. -# The -u flag specifies that this is a posix 'standard utility'. -# The rest of the line specifies the command name or names used to run -# the command. - -bltincmd -u command -bgcmd -j -u bg -breakcmd -s break -s continue -cdcmd -u cd chdir -dotcmd -s . -echocmd echo -evalcmd -s eval -execcmd -s exec -exitcmd -s exit -expcmd exp let -exportcmd -s export -s readonly -falsecmd -u false -#if WITH_HISTORY -histcmd -h -u fc -inputrc inputrc -#endif -fgcmd -j -u fg -getoptscmd -u getopts -hashcmd hash -jobidcmd jobid -jobscmd -u jobs -localcmd local -#ifndef SMALL -##printfcmd printf -#endif -pwdcmd -u pwd -readcmd -u read -returncmd -s return -setcmd -s set -setvarcmd setvar -shiftcmd -s shift -timescmd -s times -trapcmd -s trap -truecmd -s : -u true -typecmd type -umaskcmd -u umask -unaliascmd -u unalias -unsetcmd -s unset -waitcmd -u wait -aliascmd -u alias -ulimitcmd ulimit -##testcmd test [ -##killcmd -u kill # mandated by posix for 'kill %job' -wordexpcmd wordexp -#newgrp -u newgrp # optional command in posix - -#exprcmd expr diff --git a/sh/builtins.h b/sh/builtins.h deleted file mode 100644 index 1f9e45a..0000000 --- a/sh/builtins.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * This file was generated by the mkbuiltins program. - */ - -#include <sys/cdefs.h> - -struct builtincmd { - const char *name; - int (*builtin)(int, char **); -}; - -extern const struct builtincmd builtincmd[]; -extern const struct builtincmd splbltincmd[]; - - -int bltincmd(int, char **); -int bgcmd(int, char **); -int breakcmd(int, char **); -int cdcmd(int, char **); -int dotcmd(int, char **); -int echocmd(int, char **); -int evalcmd(int, char **); -int execcmd(int, char **); -int exitcmd(int, char **); -int expcmd(int, char **); -int exportcmd(int, char **); -int falsecmd(int, char **); -#if WITH_HISTORY -int histcmd(int, char **); -int inputrc(int, char **); -#endif -int fgcmd(int, char **); -int getoptscmd(int, char **); -int hashcmd(int, char **); -int jobidcmd(int, char **); -int jobscmd(int, char **); -int localcmd(int, char **); -#ifndef SMALL -#endif -int pwdcmd(int, char **); -int readcmd(int, char **); -int returncmd(int, char **); -int setcmd(int, char **); -int setvarcmd(int, char **); -int shiftcmd(int, char **); -int timescmd(int, char **); -int trapcmd(int, char **); -int truecmd(int, char **); -int typecmd(int, char **); -int umaskcmd(int, char **); -int unaliascmd(int, char **); -int unsetcmd(int, char **); -int waitcmd(int, char **); -int aliascmd(int, char **); -int ulimitcmd(int, char **); -int wordexpcmd(int, char **); diff --git a/sh/cd.c b/sh/cd.c deleted file mode 100644 index 4ab599b..0000000 --- a/sh/cd.c +++ /dev/null @@ -1,446 +0,0 @@ -/* $NetBSD: cd.c,v 1.34 2003/11/14 20:00:28 dsl 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 - * Kenneth Almquist. - * - * 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[] = "@(#)cd.c 8.2 (Berkeley) 5/4/95"; -#else -__RCSID("$NetBSD: cd.c,v 1.34 2003/11/14 20:00:28 dsl Exp $"); -#endif -#endif /* not lint */ - -#include <sys/types.h> -#include <sys/stat.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <errno.h> - -/* - * The cd and pwd commands. - */ - -#include "shell.h" -#include "var.h" -#include "nodes.h" /* for jobs.h */ -#include "jobs.h" -#include "options.h" -#include "output.h" -#include "memalloc.h" -#include "error.h" -#include "exec.h" -#include "redir.h" -#include "mystring.h" -#include "show.h" -#include "cd.h" - -STATIC int docd(char *, int); -STATIC char *getcomponent(void); -STATIC void updatepwd(char *); -STATIC void find_curdir(int noerror); - -char *curdir = NULL; /* current working directory */ -char *prevdir; /* previous working directory */ -STATIC char *cdcomppath; - -int -cdcmd(int argc, char **argv) -{ - const char *dest; - const char *path; - char *p, *d; - struct stat statb; - int print = cdprint; /* set -cdprint to enable */ - - nextopt(nullstr); - - /* - * Try (quite hard) to have 'curdir' defined, nothing has set - * it on entry to the shell, but we want 'cd fred; cd -' to work. - */ - getpwd(1); - dest = *argptr; - if (dest == NULL) { - dest = bltinlookup("HOME", 1); - if (dest == NULL) - error("HOME not set"); - } else { - if (argptr[1]) { - /* Do 'ksh' style substitution */ - if (!curdir) - error("PWD not set"); - p = strstr(curdir, dest); - if (!p) - error("bad substitution"); - d = stalloc(strlen(curdir) + strlen(argptr[1]) + 1); - memcpy(d, curdir, p - curdir); - strcpy(d + (p - curdir), argptr[1]); - strcat(d, p + strlen(dest)); - dest = d; - print = 1; - } - } - - if (dest[0] == '-' && dest[1] == '\0') { - dest = prevdir ? prevdir : curdir; - print = 1; - } - if (*dest == '\0') - dest = "."; - if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL) - path = nullstr; - while ((p = padvance(&path, dest)) != NULL) { - if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { - if (!print) { - /* - * XXX - rethink - */ - if (p[0] == '.' && p[1] == '/' && p[2] != '\0') - p += 2; - print = strcmp(p, dest); - } - if (docd(p, print) >= 0) - return 0; - - } - } - error("can't cd to %s", dest); - /* NOTREACHED */ -} - - -/* - * Actually do the chdir. In an interactive shell, print the - * directory name if "print" is nonzero. - */ - -STATIC int -docd(char *dest, int print) -{ - char *p; - char *q; - char *component; - struct stat statb; - int first; - int badstat; - - TRACE(("docd(\"%s\", %d) called\n", dest, print)); - - /* - * Check each component of the path. If we find a symlink or - * something we can't stat, clear curdir to force a getcwd() - * next time we get the value of the current directory. - */ - badstat = 0; - cdcomppath = stalloc(strlen(dest) + 1); - scopy(dest, cdcomppath); - STARTSTACKSTR(p); - if (*dest == '/') { - STPUTC('/', p); - cdcomppath++; - } - first = 1; - while ((q = getcomponent()) != NULL) { - if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0')) - continue; - if (! first) - STPUTC('/', p); - first = 0; - component = q; - while (*q) - STPUTC(*q++, p); - if (equal(component, "..")) - continue; - STACKSTRNUL(p); - if ((lstat(stackblock(), &statb) < 0) - || (S_ISLNK(statb.st_mode))) { - /* print = 1; */ - badstat = 1; - break; - } - } - - INTOFF; - if (chdir(dest) < 0) { - INTON; - return -1; - } - updatepwd(badstat ? NULL : dest); - INTON; - if (print && iflag && curdir) - out1fmt("%s\n", curdir); - return 0; -} - - -/* - * Get the next component of the path name pointed to by cdcomppath. - * This routine overwrites the string pointed to by cdcomppath. - */ - -STATIC char * -getcomponent() -{ - char *p; - char *start; - - if ((p = cdcomppath) == NULL) - return NULL; - start = cdcomppath; - while (*p != '/' && *p != '\0') - p++; - if (*p == '\0') { - cdcomppath = NULL; - } else { - *p++ = '\0'; - cdcomppath = p; - } - return start; -} - - - -/* - * Update curdir (the name of the current directory) in response to a - * cd command. We also call hashcd to let the routines in exec.c know - * that the current directory has changed. - */ - -STATIC void -updatepwd(char *dir) -{ - char *new; - char *p; - - hashcd(); /* update command hash table */ - - /* - * If our argument is NULL, we don't know the current directory - * any more because we traversed a symbolic link or something - * we couldn't stat(). - */ - if (dir == NULL || curdir == NULL) { - if (prevdir) - ckfree(prevdir); - INTOFF; - prevdir = curdir; - curdir = NULL; - getpwd(1); - INTON; - if (curdir) - setvar("PWD", curdir, VEXPORT); - else - unsetvar("PWD", 0); - return; - } - cdcomppath = stalloc(strlen(dir) + 1); - scopy(dir, cdcomppath); - STARTSTACKSTR(new); - if (*dir != '/') { - p = curdir; - while (*p) - STPUTC(*p++, new); - if (p[-1] == '/') - STUNPUTC(new); - } - while ((p = getcomponent()) != NULL) { - if (equal(p, "..")) { - while (new > stackblock() && (STUNPUTC(new), *new) != '/'); - } else if (*p != '\0' && ! equal(p, ".")) { - STPUTC('/', new); - while (*p) - STPUTC(*p++, new); - } - } - if (new == stackblock()) - STPUTC('/', new); - STACKSTRNUL(new); - INTOFF; - if (prevdir) - ckfree(prevdir); - prevdir = curdir; - curdir = savestr(stackblock()); - setvar("PWD", curdir, VEXPORT); - INTON; -} - -/* - * Posix says the default should be 'pwd -L' (as below), however - * the 'cd' command (above) does something much nearer to the - * posix 'cd -P' (not the posix default of 'cd -L'). - * If 'cd' is changed to support -P/L then the default here - * needs to be revisited if the historic behaviour is to be kept. - */ - -int -pwdcmd(int argc, char **argv) -{ - int i; - char opt = 'L'; - - while ((i = nextopt("LP")) != '\0') - opt = i; - if (*argptr) - error("unexpected argument"); - - if (opt == 'L') - getpwd(0); - else - find_curdir(0); - - setvar("PWD", curdir, VEXPORT); - out1str(curdir); - out1c('\n'); - return 0; -} - - - - -#define MAXPWD 256 - -/* - * Find out what the current directory is. If we already know the current - * directory, this routine returns immediately. - */ -void -getpwd(int noerror) -{ - char *pwd; - struct stat stdot, stpwd; - static int first = 1; - - if (curdir) - return; - - if (first) { - first = 0; - pwd = getenv("PWD"); - if (pwd && *pwd == '/' && stat(".", &stdot) != -1 && - stat(pwd, &stpwd) != -1 && - stdot.st_dev == stpwd.st_dev && - stdot.st_ino == stpwd.st_ino) { - curdir = savestr(pwd); - return; - } - } - - find_curdir(noerror); - - return; -} - -STATIC void -find_curdir(int noerror) -{ - int i; - char *pwd; - - /* - * Things are a bit complicated here; we could have just used - * getcwd, but traditionally getcwd is implemented using popen - * to /bin/pwd. This creates a problem for us, since we cannot - * keep track of the job if it is being ran behind our backs. - * So we re-implement getcwd(), and we suppress interrupts - * throughout the process. This is not completely safe, since - * the user can still break out of it by killing the pwd program. - * We still try to use getcwd for systems that we know have a - * c implementation of getcwd, that does not open a pipe to - * /bin/pwd. - */ -#if defined(__NetBSD__) || defined(__SVR4) || defined(__linux__) - for (i = MAXPWD;; i *= 2) { - pwd = stalloc(i); - if (getcwd(pwd, i) != NULL) { - curdir = savestr(pwd); - return; - } - stunalloc(pwd); - if (errno == ERANGE) - continue; - if (!noerror) - error("getcwd() failed: %s", strerror(errno)); - return; - } -#else - { - char *p; - int status; - struct job *jp; - int pip[2]; - - pwd = stalloc(MAXPWD); - INTOFF; - if (pipe(pip) < 0) - error("Pipe call failed"); - jp = makejob((union node *)NULL, 1); - if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) { - (void) close(pip[0]); - if (pip[1] != 1) { - close(1); - copyfd(pip[1], 1); - close(pip[1]); - } - (void) execl("/bin/pwd", "pwd", (char *)0); - sh_warn("Cannot exec /bin/pwd"); - exit(1); - } - (void) close(pip[1]); - pip[1] = -1; - p = pwd; - while ((i = read(pip[0], p, pwd + MAXPWD - p)) > 0 - || (i == -1 && errno == EINTR)) { - if (i > 0) - p += i; - } - (void) close(pip[0]); - pip[0] = -1; - status = waitforjob(jp); - if (status != 0) - error((char *)0); - if (i < 0 || p == pwd || p[-1] != '\n') { - if (noerror) { - INTON; - return; - } - error("pwd command failed"); - } - p[-1] = '\0'; - INTON; - curdir = savestr(pwd); - return; - } -#endif -} diff --git a/sh/cd.h b/sh/cd.h deleted file mode 100644 index a4dcc01..0000000 --- a/sh/cd.h +++ /dev/null @@ -1,35 +0,0 @@ -/* $NetBSD: cd.h,v 1.4 2003/08/07 09:05:30 agc Exp $ */ - -/*- - * Copyright (c) 1995 - * 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. - * - */ - -void getpwd(int); -int cdcmd(int, char **); -int pwdcmd(int, char **); diff --git a/sh/error.c b/sh/error.c deleted file mode 100644 index 8cbed19..0000000 --- a/sh/error.c +++ /dev/null @@ -1,366 +0,0 @@ -/* $NetBSD: error.c,v 1.31 2003/08/07 09:05:30 agc 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 - * Kenneth Almquist. - * - * 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[] = "@(#)error.c 8.2 (Berkeley) 5/4/95"; -#else -__RCSID("$NetBSD: error.c,v 1.31 2003/08/07 09:05:30 agc Exp $"); -#endif -#endif /* not lint */ - -/* - * Errors and exceptions. - */ - -#include <signal.h> -#include <stdlib.h> -#include <unistd.h> -#include <errno.h> -#include <stdio.h> -#include <string.h> - -#include "shell.h" -#include "main.h" -#include "options.h" -#include "output.h" -#include "error.h" -#include "show.h" - -#define signal bsd_signal -/* - * Code to handle exceptions in C. - */ - -struct jmploc *handler; -int exception; -volatile int suppressint; -volatile int intpending; -char *commandname; - - -static void exverror(int, const char *, va_list) - __attribute__((__noreturn__)); - -/* - * Called to raise an exception. Since C doesn't include exceptions, we - * just do a longjmp to the exception handler. The type of exception is - * stored in the global variable "exception". - */ - -void -exraise(int e) -{ - if (handler == NULL) - abort(); - exception = e; - longjmp(handler->loc, 1); -} - - -/* - * Called from trap.c when a SIGINT is received. (If the user specifies - * that SIGINT is to be trapped or ignored using the trap builtin, then - * this routine is not called.) Suppressint is nonzero when interrupts - * are held using the INTOFF macro. The call to _exit is necessary because - * there is a short period after a fork before the signal handlers are - * set to the appropriate value for the child. (The test for iflag is - * just defensive programming.) - */ - -void -onint(void) -{ - sigset_t nsigset; - - if (suppressint) { - intpending = 1; - return; - } - intpending = 0; - sigemptyset(&nsigset); - sigprocmask(SIG_SETMASK, &nsigset, NULL); - if (rootshell && iflag) - exraise(EXINT); - else { - signal(SIGINT, SIG_DFL); - raise(SIGINT); - } - /* NOTREACHED */ -} - -static void -exvwarning(int sv_errno, const char *msg, va_list ap) -{ - /* Partially emulate line buffered output so that: - * printf '%d\n' 1 a 2 - * and - * printf '%d %d %d\n' 1 a 2 - * both generate sensible text when stdout and stderr are merged. - */ - if (output.nextc != output.buf && output.nextc[-1] == '\n') - flushout(&output); - if (commandname) - outfmt(&errout, "%s: ", commandname); - if (msg != NULL) { - doformat(&errout, msg, ap); - if (sv_errno >= 0) - outfmt(&errout, ": "); - } - if (sv_errno >= 0) - outfmt(&errout, "%s", strerror(sv_errno)); - out2c('\n'); - flushout(&errout); -} - -/* - * Exverror is called to raise the error exception. If the second argument - * is not NULL then error prints an error message using printf style - * formatting. It then raises the error exception. - */ -static void -exverror(int cond, const char *msg, va_list ap) -{ - CLEAR_PENDING_INT; - INTOFF; - -#ifdef DEBUG - if (msg) { - TRACE(("exverror(%d, \"", cond)); - TRACEV((msg, ap)); - TRACE(("\") pid=%d\n", getpid())); - } else - TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid())); -#endif - if (msg) - exvwarning(-1, msg, ap); - - flushall(); - exraise(cond); - /* NOTREACHED */ -} - - -void -error(const char *msg, ...) -{ - va_list ap; - - va_start(ap, msg); - exverror(EXERROR, msg, ap); - /* NOTREACHED */ - va_end(ap); -} - - -void -exerror(int cond, const char *msg, ...) -{ - va_list ap; - - va_start(ap, msg); - exverror(cond, msg, ap); - /* NOTREACHED */ - va_end(ap); -} - -/* - * error/warning routines for external builtins - */ - -void -sh_exit(int rval) -{ - exerrno = rval & 255; - exraise(EXEXEC); -} - -void -sh_err(int status, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - exvwarning(errno, fmt, ap); - va_end(ap); - sh_exit(status); -} - -void -sh_verr(int status, const char *fmt, va_list ap) -{ - exvwarning(errno, fmt, ap); - sh_exit(status); -} - -void -sh_errx(int status, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - exvwarning(-1, fmt, ap); - va_end(ap); - sh_exit(status); -} - -void -sh_verrx(int status, const char *fmt, va_list ap) -{ - exvwarning(-1, fmt, ap); - sh_exit(status); -} - -void -sh_warn(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - exvwarning(errno, fmt, ap); - va_end(ap); -} - -void -sh_vwarn(const char *fmt, va_list ap) -{ - exvwarning(errno, fmt, ap); -} - -void -sh_warnx(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - exvwarning(-1, fmt, ap); - va_end(ap); -} - -void -sh_vwarnx(const char *fmt, va_list ap) -{ - exvwarning(-1, fmt, ap); -} - - -/* - * Table of error messages. - */ - -struct errname { - short errcode; /* error number */ - short action; /* operation which encountered the error */ - const char *msg; /* text describing the error */ -}; - - -#define ALL (E_OPEN|E_CREAT|E_EXEC) - -STATIC const struct errname errormsg[] = { - { EINTR, ALL, "interrupted" }, - { EACCES, ALL, "permission denied" }, - { EIO, ALL, "I/O error" }, - { EEXIST, ALL, "file exists" }, - { ENOENT, E_OPEN, "no such file" }, - { ENOENT, E_CREAT,"directory nonexistent" }, - { ENOENT, E_EXEC, "not found" }, - { ENOTDIR, E_OPEN, "no such file" }, - { ENOTDIR, E_CREAT,"directory nonexistent" }, - { ENOTDIR, E_EXEC, "not found" }, - { EISDIR, ALL, "is a directory" }, -#ifdef EMFILE - { EMFILE, ALL, "too many open files" }, -#endif - { ENFILE, ALL, "file table overflow" }, - { ENOSPC, ALL, "file system full" }, -#ifdef EDQUOT - { EDQUOT, ALL, "disk quota exceeded" }, -#endif -#ifdef ENOSR - { ENOSR, ALL, "no streams resources" }, -#endif - { ENXIO, ALL, "no such device or address" }, - { EROFS, ALL, "read-only file system" }, - { ETXTBSY, ALL, "text busy" }, -#ifdef EAGAIN - { EAGAIN, E_EXEC, "not enough memory" }, -#endif - { ENOMEM, ALL, "not enough memory" }, -#ifdef ENOLINK - { ENOLINK, ALL, "remote access failed" }, -#endif -#ifdef EMULTIHOP - { EMULTIHOP, ALL, "remote access failed" }, -#endif -#ifdef ECOMM - { ECOMM, ALL, "remote access failed" }, -#endif -#ifdef ESTALE - { ESTALE, ALL, "remote access failed" }, -#endif -#ifdef ETIMEDOUT - { ETIMEDOUT, ALL, "remote access failed" }, -#endif -#ifdef ELOOP - { ELOOP, ALL, "symbolic link loop" }, -#endif - { E2BIG, E_EXEC, "argument list too long" }, -#ifdef ELIBACC - { ELIBACC, E_EXEC, "shared library missing" }, -#endif - { 0, 0, NULL }, -}; - - -/* - * Return a string describing an error. The returned string may be a - * pointer to a static buffer that will be overwritten on the next call. - * Action describes the operation that got the error. - */ - -const char * -errmsg(int e, int action) -{ - struct errname const *ep; - static char buf[12]; - - for (ep = errormsg ; ep->errcode ; ep++) { - if (ep->errcode == e && (ep->action & action) != 0) - return ep->msg; - } - fmtstr(buf, sizeof buf, "error %d", e); - return buf; -} diff --git a/sh/error.h b/sh/error.h deleted file mode 100644 index 8e70ca4..0000000 --- a/sh/error.h +++ /dev/null @@ -1,117 +0,0 @@ -/* $NetBSD: error.h,v 1.16 2003/08/07 09:05:30 agc 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 - * Kenneth Almquist. - * - * 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. - * - * @(#)error.h 8.2 (Berkeley) 5/4/95 - */ - -#include <stdarg.h> - -/* - * Types of operations (passed to the errmsg routine). - */ - -#define E_OPEN 01 /* opening a file */ -#define E_CREAT 02 /* creating a file */ -#define E_EXEC 04 /* executing a program */ - - -/* - * We enclose jmp_buf in a structure so that we can declare pointers to - * jump locations. The global variable handler contains the location to - * jump to when an exception occurs, and the global variable exception - * contains a code identifying the exeception. To implement nested - * exception handlers, the user should save the value of handler on entry - * to an inner scope, set handler to point to a jmploc structure for the - * inner scope, and restore handler on exit from the scope. - */ - -#include <setjmp.h> - -struct jmploc { - jmp_buf loc; -}; - -extern struct jmploc *handler; -extern int exception; -extern int exerrno; /* error for EXEXEC */ - -/* exceptions */ -#define EXINT 0 /* SIGINT received */ -#define EXERROR 1 /* a generic error */ -#define EXSHELLPROC 2 /* execute a shell procedure */ -#define EXEXEC 3 /* command execution failed */ - - -/* - * These macros allow the user to suspend the handling of interrupt signals - * over a period of time. This is similar to SIGHOLD to or sigblock, but - * much more efficient and portable. (But hacking the kernel is so much - * more fun than worrying about efficiency and portability. :-)) - */ - -extern volatile int suppressint; -extern volatile int intpending; - -#define INTOFF suppressint++ -#define INTON { if (--suppressint == 0 && intpending) onint(); } -#define FORCEINTON {suppressint = 0; if (intpending) onint();} -#define CLEAR_PENDING_INT intpending = 0 -#define int_pending() intpending - -void exraise(int) __attribute__((__noreturn__)); -void onint(void); -void error(const char *, ...) __attribute__((__noreturn__)); -void exerror(int, const char *, ...) __attribute__((__noreturn__)); -const char *errmsg(int, int); - -void sh_err(int, const char *, ...) __attribute__((__noreturn__)); -void sh_verr(int, const char *, va_list) __attribute__((__noreturn__)); -void sh_errx(int, const char *, ...) __attribute__((__noreturn__)); -void sh_verrx(int, const char *, va_list) __attribute__((__noreturn__)); -void sh_warn(const char *, ...); -void sh_vwarn(const char *, va_list); -void sh_warnx(const char *, ...); -void sh_vwarnx(const char *, va_list); - -void sh_exit(int) __attribute__((__noreturn__)); - - -/* - * BSD setjmp saves the signal mask, which violates ANSI C and takes time, - * so we use _setjmp instead. - */ - -#if defined(BSD) && !defined(__SVR4) && !defined(__linux__) -#define setjmp(jmploc) _setjmp(jmploc) -#define longjmp(jmploc, val) _longjmp(jmploc, val) -#endif diff --git a/sh/eval.c b/sh/eval.c deleted file mode 100644 index 4eb7ded..0000000 --- a/sh/eval.c +++ /dev/null @@ -1,1257 +0,0 @@ -/* $NetBSD: eval.c,v 1.81.2.1 2005/06/13 22:03:51 tron Exp $ */ - -/*- - * Copyright (c) 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * 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[] = "@(#)eval.c 8.9 (Berkeley) 6/8/95"; -#else -__RCSID("$NetBSD: eval.c,v 1.81.2.1 2005/06/13 22:03:51 tron Exp $"); -#endif -#endif /* not lint */ - -#include <stdlib.h> -#include <signal.h> -#include <stdio.h> -#include <unistd.h> -#ifdef __linux__ -#include <fcntl.h> -#else -#include <sys/fcntl.h> -#endif -#include <sys/times.h> -#include <sys/param.h> -#include <sys/types.h> -#include <sys/wait.h> - -/* - * Evaluate a command. - */ - -#include "shell.h" -#include "nodes.h" -#include "syntax.h" -#include "expand.h" -#include "parser.h" -#include "jobs.h" -#include "eval.h" -#include "builtins.h" -#include "options.h" -#include "exec.h" -#include "redir.h" -#include "input.h" -#include "output.h" -#include "trap.h" -#include "var.h" -#include "memalloc.h" -#include "error.h" -#include "show.h" -#include "mystring.h" -#include "main.h" -#ifndef SMALL -#include "myhistedit.h" -#endif - - -/* flags in argument to evaltree */ -#define EV_EXIT 01 /* exit after evaluating tree */ -#define EV_TESTED 02 /* exit status is checked; ignore -e flag */ -#define EV_BACKCMD 04 /* command executing within back quotes */ - -int evalskip; /* set if we are skipping commands */ -STATIC int skipcount; /* number of levels to skip */ -MKINIT int loopnest; /* current loop nesting level */ -int funcnest; /* depth of function calls */ - - -char *commandname; -struct strlist *cmdenviron; -int exitstatus; /* exit status of last command */ -int back_exitstatus; /* exit status of backquoted command */ - - -STATIC void evalloop(union node *, int); -STATIC void evalfor(union node *, int); -STATIC void evalcase(union node *, int); -STATIC void evalsubshell(union node *, int); -STATIC void expredir(union node *); -STATIC void evalpipe(union node *); -STATIC void evalcommand(union node *, int, struct backcmd *); -STATIC void prehash(union node *); - - -/* - * Called to reset things after an exception. - */ - -#ifdef mkinit -INCLUDE "eval.h" - -RESET { - evalskip = 0; - loopnest = 0; - funcnest = 0; -} - -SHELLPROC { - exitstatus = 0; -} -#endif - -static int -sh_pipe(int fds[2]) -{ - int nfd; - - if (pipe(fds)) - return -1; - - if (fds[0] < 3) { - nfd = fcntl(fds[0], F_DUPFD, 3); - if (nfd != -1) { - close(fds[0]); - fds[0] = nfd; - } - } - - if (fds[1] < 3) { - nfd = fcntl(fds[1], F_DUPFD, 3); - if (nfd != -1) { - close(fds[1]); - fds[1] = nfd; - } - } - return 0; -} - - -/* - * The eval commmand. - */ - -int -evalcmd(int argc, char **argv) -{ - char *p; - char *concat; - char **ap; - - if (argc > 1) { - p = argv[1]; - if (argc > 2) { - STARTSTACKSTR(concat); - ap = argv + 2; - for (;;) { - while (*p) - STPUTC(*p++, concat); - if ((p = *ap++) == NULL) - break; - STPUTC(' ', concat); - } - STPUTC('\0', concat); - p = grabstackstr(concat); - } - evalstring(p, EV_TESTED); - } - return exitstatus; -} - - -/* - * Execute a command or commands contained in a string. - */ - -void -evalstring(char *s, int flag) -{ - union node *n; - struct stackmark smark; - - setstackmark(&smark); - setinputstring(s, 1); - - while ((n = parsecmd(0)) != NEOF) { - evaltree(n, flag); - popstackmark(&smark); - } - popfile(); - popstackmark(&smark); -} - - - -/* - * Evaluate a parse tree. The value is left in the global variable - * exitstatus. - */ - -void -evaltree(union node *n, int flags) -{ - if (n == NULL) { - TRACE(("evaltree(NULL) called\n")); - exitstatus = 0; - goto out; - } -#ifdef WITH_HISTORY - displayhist = 1; /* show history substitutions done with fc */ -#endif - TRACE(("pid %d, evaltree(%p: %d, %d) called\n", - getpid(), n, n->type, flags)); - switch (n->type) { - case NSEMI: - evaltree(n->nbinary.ch1, flags & EV_TESTED); - if (evalskip) - goto out; - evaltree(n->nbinary.ch2, flags); - break; - case NAND: - evaltree(n->nbinary.ch1, EV_TESTED); - if (evalskip || exitstatus != 0) - goto out; - evaltree(n->nbinary.ch2, flags); - break; - case NOR: - evaltree(n->nbinary.ch1, EV_TESTED); - if (evalskip || exitstatus == 0) - goto out; - evaltree(n->nbinary.ch2, flags); - break; - case NREDIR: - expredir(n->nredir.redirect); - redirect(n->nredir.redirect, REDIR_PUSH); - evaltree(n->nredir.n, flags); - popredir(); - break; - case NSUBSHELL: - evalsubshell(n, flags); - break; - case NBACKGND: - evalsubshell(n, flags); - break; - case NIF: { - evaltree(n->nif.test, EV_TESTED); - if (evalskip) - goto out; - if (exitstatus == 0) - evaltree(n->nif.ifpart, flags); - else if (n->nif.elsepart) - evaltree(n->nif.elsepart, flags); - else - exitstatus = 0; - break; - } - case NWHILE: - case NUNTIL: - evalloop(n, flags); - break; - case NFOR: - evalfor(n, flags); - break; - case NCASE: - evalcase(n, flags); - break; - case NDEFUN: - defun(n->narg.text, n->narg.next); - exitstatus = 0; - break; - case NNOT: - evaltree(n->nnot.com, EV_TESTED); - exitstatus = !exitstatus; - break; - case NPIPE: - evalpipe(n); - break; - case NCMD: - evalcommand(n, flags, (struct backcmd *)NULL); - break; - default: - out1fmt("Node type = %d\n", n->type); - flushout(&output); - break; - } -out: - if (pendingsigs) - dotrap(); - if ((flags & EV_EXIT) != 0) - exitshell(exitstatus); -} - - -STATIC void -evalloop(union node *n, int flags) -{ - int status; - - loopnest++; - status = 0; - for (;;) { - evaltree(n->nbinary.ch1, EV_TESTED); - if (evalskip) { -skipping: if (evalskip == SKIPCONT && --skipcount <= 0) { - evalskip = 0; - continue; - } - if (evalskip == SKIPBREAK && --skipcount <= 0) - evalskip = 0; - break; - } - if (n->type == NWHILE) { - if (exitstatus != 0) - break; - } else { - if (exitstatus == 0) - break; - } - evaltree(n->nbinary.ch2, flags & EV_TESTED); - status = exitstatus; - if (evalskip) - goto skipping; - } - loopnest--; - exitstatus = status; -} - - - -STATIC void -evalfor(union node *n, int flags) -{ - struct arglist arglist; - union node *argp; - struct strlist *sp; - struct stackmark smark; - int status = 0; - - setstackmark(&smark); - arglist.lastp = &arglist.list; - for (argp = n->nfor.args ; argp ; argp = argp->narg.next) { - expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); - if (evalskip) - goto out; - } - *arglist.lastp = NULL; - - loopnest++; - for (sp = arglist.list ; sp ; sp = sp->next) { - setvar(n->nfor.var, sp->text, 0); - evaltree(n->nfor.body, flags & EV_TESTED); - status = exitstatus; - if (evalskip) { - if (evalskip == SKIPCONT && --skipcount <= 0) { - evalskip = 0; - continue; - } - if (evalskip == SKIPBREAK && --skipcount <= 0) - evalskip = 0; - break; - } - } - loopnest--; - exitstatus = status; -out: - popstackmark(&smark); -} - - - -STATIC void -evalcase(union node *n, int flags) -{ - union node *cp; - union node *patp; - struct arglist arglist; - struct stackmark smark; - int status = 0; - - setstackmark(&smark); - arglist.lastp = &arglist.list; - expandarg(n->ncase.expr, &arglist, EXP_TILDE); - for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) { - for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) { - if (casematch(patp, arglist.list->text)) { - if (evalskip == 0) { - evaltree(cp->nclist.body, flags); - status = exitstatus; - } - goto out; - } - } - } -out: - exitstatus = status; - popstackmark(&smark); -} - - - -/* - * Kick off a subshell to evaluate a tree. - */ - -STATIC void -evalsubshell(union node *n, int flags) -{ - struct job *jp; - int backgnd = (n->type == NBACKGND); - - expredir(n->nredir.redirect); - INTOFF; - jp = makejob(n, 1); - if (forkshell(jp, n, backgnd) == 0) { - INTON; - if (backgnd) - flags &=~ EV_TESTED; - redirect(n->nredir.redirect, 0); - /* never returns */ - evaltree(n->nredir.n, flags | EV_EXIT); - } - if (! backgnd) - exitstatus = waitforjob(jp); - INTON; -} - - - -/* - * Compute the names of the files in a redirection list. - */ - -STATIC void -expredir(union node *n) -{ - union node *redir; - - for (redir = n ; redir ; redir = redir->nfile.next) { - struct arglist fn; - fn.lastp = &fn.list; - switch (redir->type) { - case NFROMTO: - case NFROM: - case NTO: - case NCLOBBER: - case NAPPEND: - expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR); - redir->nfile.expfname = fn.list->text; - break; - case NFROMFD: - case NTOFD: - if (redir->ndup.vname) { - expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE); - fixredir(redir, fn.list->text, 1); - } - break; - } - } -} - - - -/* - * Evaluate a pipeline. All the processes in the pipeline are children - * of the process creating the pipeline. (This differs from some versions - * of the shell, which make the last process in a pipeline the parent - * of all the rest.) - */ - -STATIC void -evalpipe(union node *n) -{ - struct job *jp; - struct nodelist *lp; - int pipelen; - int prevfd; - int pip[2]; - - TRACE(("evalpipe(0x%lx) called\n", (long)n)); - pipelen = 0; - for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) - pipelen++; - INTOFF; - jp = makejob(n, pipelen); - prevfd = -1; - for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { - prehash(lp->n); - pip[1] = -1; - if (lp->next) { - if (sh_pipe(pip) < 0) { - close(prevfd); - error("Pipe call failed"); - } - } - if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) { - INTON; - if (prevfd > 0) { - close(0); - copyfd(prevfd, 0); - close(prevfd); - } - if (pip[1] >= 0) { - close(pip[0]); - if (pip[1] != 1) { - close(1); - copyfd(pip[1], 1); - close(pip[1]); - } - } - evaltree(lp->n, EV_EXIT); - } - if (prevfd >= 0) - close(prevfd); - prevfd = pip[0]; - close(pip[1]); - } - if (n->npipe.backgnd == 0) { - exitstatus = waitforjob(jp); - TRACE(("evalpipe: job done exit status %d\n", exitstatus)); - } - INTON; -} - - - -/* - * Execute a command inside back quotes. If it's a builtin command, we - * want to save its output in a block obtained from malloc. Otherwise - * we fork off a subprocess and get the output of the command via a pipe. - * Should be called with interrupts off. - */ - -void -evalbackcmd(union node *n, struct backcmd *result) -{ - int pip[2]; - struct job *jp; - struct stackmark smark; /* unnecessary */ - - setstackmark(&smark); - result->fd = -1; - result->buf = NULL; - result->nleft = 0; - result->jp = NULL; - if (n == NULL) { - goto out; - } -#ifdef notyet - /* - * For now we disable executing builtins in the same - * context as the shell, because we are not keeping - * enough state to recover from changes that are - * supposed only to affect subshells. eg. echo "`cd /`" - */ - if (n->type == NCMD) { - exitstatus = oexitstatus; - evalcommand(n, EV_BACKCMD, result); - } else -#endif - { - INTOFF; - if (sh_pipe(pip) < 0) - error("Pipe call failed"); - jp = makejob(n, 1); - if (forkshell(jp, n, FORK_NOJOB) == 0) { - FORCEINTON; - close(pip[0]); - if (pip[1] != 1) { - close(1); - copyfd(pip[1], 1); - close(pip[1]); - } - eflag = 0; - evaltree(n, EV_EXIT); - /* NOTREACHED */ - } - close(pip[1]); - result->fd = pip[0]; - result->jp = jp; - INTON; - } -out: - popstackmark(&smark); - TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n", - result->fd, result->buf, result->nleft, result->jp)); -} - -static const char * -syspath(void) -{ - static char *sys_path = NULL; -#ifndef __linux__ - static int mib[] = {CTL_USER, USER_CS_PATH}; -#endif - static char def_path[] = "PATH=/usr/bin:/bin:/usr/sbin:/sbin"; - - if (sys_path == NULL) { -#ifndef __linux__ - size_t len; - if (sysctl(mib, 2, 0, &len, 0, 0) != -1 && - (sys_path = ckmalloc(len + 5)) != NULL && - sysctl(mib, 2, sys_path + 5, &len, 0, 0) != -1) { - memcpy(sys_path, "PATH=", 5); - } else -#endif - { - ckfree(sys_path); - /* something to keep things happy */ - sys_path = def_path; - } - } - return sys_path; -} - -static int -parse_command_args(int argc, char **argv, int *use_syspath) -{ - int sv_argc = argc; - char *cp, c; - - *use_syspath = 0; - - for (;;) { - argv++; - if (--argc == 0) - break; - cp = *argv; - if (*cp++ != '-') - break; - if (*cp == '-' && cp[1] == 0) { - argv++; - argc--; - break; - } - while ((c = *cp++)) { - switch (c) { - case 'p': - *use_syspath = 1; - break; - default: - /* run 'typecmd' for other options */ - return 0; - } - } - } - return sv_argc - argc; -} - -int vforked = 0; - -/* - * Execute a simple command. - */ - -STATIC void -evalcommand(union node *cmd, int flags, struct backcmd *backcmd) -{ - struct stackmark smark; - union node *argp; - struct arglist arglist; - struct arglist varlist; - char **argv; - int argc; - char **envp; - int varflag; - struct strlist *sp; - int mode; - int pip[2]; - struct cmdentry cmdentry; - struct job *jp; - struct jmploc jmploc; - struct jmploc *volatile savehandler = 0; - char *volatile savecmdname; - volatile struct shparam saveparam; - struct localvar *volatile savelocalvars; - volatile int e; - char *lastarg; - const char *path = pathval(); - volatile int temp_path = 0; -#if __GNUC__ - /* Avoid longjmp clobbering */ - (void) &argv; - (void) &argc; - (void) &lastarg; - (void) &flags; -#endif - - vforked = 0; - /* First expand the arguments. */ - TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); - setstackmark(&smark); - back_exitstatus = 0; - - arglist.lastp = &arglist.list; - varflag = 1; - /* Expand arguments, ignoring the initial 'name=value' ones */ - for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { - char *p = argp->narg.text; - if (varflag && is_name(*p)) { - do { - p++; - } while (is_in_name(*p)); - if (*p == '=') - continue; - } - expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); - varflag = 0; - } - *arglist.lastp = NULL; - - expredir(cmd->ncmd.redirect); - - /* Now do the initial 'name=value' ones we skipped above */ - varlist.lastp = &varlist.list; - for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { - char *p = argp->narg.text; - if (!is_name(*p)) - break; - do - p++; - while (is_in_name(*p)); - if (*p != '=') - break; - expandarg(argp, &varlist, EXP_VARTILDE); - } - *varlist.lastp = NULL; - - argc = 0; - for (sp = arglist.list ; sp ; sp = sp->next) - argc++; - argv = stalloc(sizeof (char *) * (argc + 1)); - - for (sp = arglist.list ; sp ; sp = sp->next) { - TRACE(("evalcommand arg: %s\n", sp->text)); - *argv++ = sp->text; - } - *argv = NULL; - lastarg = NULL; - if (iflag && funcnest == 0 && argc > 0) - lastarg = argv[-1]; - argv -= argc; - - /* Print the command if xflag is set. */ - if (xflag) { - char sep = 0; - out2str(ps4val()); - for (sp = varlist.list ; sp ; sp = sp->next) { - if (sep != 0) - outc(sep, &errout); - out2str(sp->text); - sep = ' '; - } - for (sp = arglist.list ; sp ; sp = sp->next) { - if (sep != 0) - outc(sep, &errout); - out2str(sp->text); - sep = ' '; - } - outc('\n', &errout); - flushout(&errout); - } - - /* Now locate the command. */ - if (argc == 0) { - cmdentry.cmdtype = CMDSPLBLTIN; - cmdentry.u.bltin = bltincmd; - } else { - static const char PATH[] = "PATH="; - int cmd_flags = DO_ERR; - - /* - * Modify the command lookup path, if a PATH= assignment - * is present - */ - for (sp = varlist.list; sp; sp = sp->next) - if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0) - path = sp->text + sizeof(PATH) - 1; - - do { - int argsused, use_syspath; - find_command(argv[0], &cmdentry, cmd_flags, path); - if (cmdentry.cmdtype == CMDUNKNOWN) { - exitstatus = 127; - flushout(&errout); - goto out; - } - - /* implement the 'command' builtin here */ - if (cmdentry.cmdtype != CMDBUILTIN || - cmdentry.u.bltin != bltincmd) - break; - cmd_flags |= DO_NOFUNC; - argsused = parse_command_args(argc, argv, &use_syspath); - if (argsused == 0) { - /* use 'type' builting to display info */ - cmdentry.u.bltin = typecmd; - break; - } - argc -= argsused; - argv += argsused; - if (use_syspath) - path = syspath() + 5; - } while (argc != 0); - if (cmdentry.cmdtype == CMDSPLBLTIN && cmd_flags & DO_NOFUNC) - /* posix mandates that 'command <splbltin>' act as if - <splbltin> was a normal builtin */ - cmdentry.cmdtype = CMDBUILTIN; - } - - /* Fork off a child process if necessary. */ - if (cmd->ncmd.backgnd - || (cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0) - || ((flags & EV_BACKCMD) != 0 - && ((cmdentry.cmdtype != CMDBUILTIN && cmdentry.cmdtype != CMDSPLBLTIN) - || cmdentry.u.bltin == dotcmd - || cmdentry.u.bltin == evalcmd))) { - INTOFF; - jp = makejob(cmd, 1); - mode = cmd->ncmd.backgnd; - if (flags & EV_BACKCMD) { - mode = FORK_NOJOB; - if (sh_pipe(pip) < 0) - error("Pipe call failed"); - } -#ifdef DO_SHAREDVFORK - /* It is essential that if DO_SHAREDVFORK is defined that the - * child's address space is actually shared with the parent as - * we rely on this. - */ - if (cmdentry.cmdtype == CMDNORMAL) { - pid_t pid; - - savelocalvars = localvars; - localvars = NULL; - vforked = 1; - switch (pid = vfork()) { - case -1: - TRACE(("Vfork failed, errno=%d\n", errno)); - INTON; - error("Cannot vfork"); - break; - case 0: - /* Make sure that exceptions only unwind to - * after the vfork(2) - */ - if (setjmp(jmploc.loc)) { - if (exception == EXSHELLPROC) { - /* We can't progress with the vfork, - * so, set vforked = 2 so the parent - * knows, and _exit(); - */ - vforked = 2; - _exit(0); - } else { - _exit(exerrno); - } - } - savehandler = handler; - handler = &jmploc; - listmklocal(varlist.list, VEXPORT | VNOFUNC); - forkchild(jp, cmd, mode, vforked); - break; - default: - handler = savehandler; /* restore from vfork(2) */ - poplocalvars(); - localvars = savelocalvars; - if (vforked == 2) { - vforked = 0; - - (void)waitpid(pid, NULL, 0); - /* We need to progress in a normal fork fashion */ - goto normal_fork; - } - vforked = 0; - forkparent(jp, cmd, mode, pid); - goto parent; - } - } else { -normal_fork: -#endif - if (forkshell(jp, cmd, mode) != 0) - goto parent; /* at end of routine */ - FORCEINTON; -#ifdef DO_SHAREDVFORK - } -#endif - if (flags & EV_BACKCMD) { - if (!vforked) { - FORCEINTON; - } - close(pip[0]); - if (pip[1] != 1) { - close(1); - copyfd(pip[1], 1); - close(pip[1]); - } - } - flags |= EV_EXIT; - } - - /* This is the child process if a fork occurred. */ - /* Execute the command. */ - switch (cmdentry.cmdtype) { - case CMDFUNCTION: -#ifdef DEBUG - trputs("Shell function: "); trargs(argv); -#endif - redirect(cmd->ncmd.redirect, REDIR_PUSH); - saveparam = shellparam; - shellparam.malloc = 0; - shellparam.reset = 1; - shellparam.nparam = argc - 1; - shellparam.p = argv + 1; - shellparam.optnext = NULL; - INTOFF; - savelocalvars = localvars; - localvars = NULL; - INTON; - if (setjmp(jmploc.loc)) { - if (exception == EXSHELLPROC) { - freeparam((volatile struct shparam *) - &saveparam); - } else { - freeparam(&shellparam); - shellparam = saveparam; - } - poplocalvars(); - localvars = savelocalvars; - handler = savehandler; - longjmp(handler->loc, 1); - } - savehandler = handler; - handler = &jmploc; - listmklocal(varlist.list, 0); - /* stop shell blowing its stack */ - if (++funcnest > 1000) - error("too many nested function calls"); - evaltree(cmdentry.u.func, flags & EV_TESTED); - funcnest--; - INTOFF; - poplocalvars(); - localvars = savelocalvars; - freeparam(&shellparam); - shellparam = saveparam; - handler = savehandler; - popredir(); - INTON; - if (evalskip == SKIPFUNC) { - evalskip = 0; - skipcount = 0; - } - if (flags & EV_EXIT) - exitshell(exitstatus); - break; - - case CMDBUILTIN: - case CMDSPLBLTIN: -#ifdef DEBUG - trputs("builtin command: "); trargs(argv); -#endif - mode = (cmdentry.u.bltin == execcmd) ? 0 : REDIR_PUSH; - if (flags == EV_BACKCMD) { - memout.nleft = 0; - memout.nextc = memout.buf; - memout.bufsize = 64; - mode |= REDIR_BACKQ; - } - e = -1; - savehandler = handler; - savecmdname = commandname; - handler = &jmploc; - if (!setjmp(jmploc.loc)) { - /* We need to ensure the command hash table isn't - * corruped by temporary PATH assignments. - * However we must ensure the 'local' command works! - */ - if (path != pathval() && (cmdentry.u.bltin == hashcmd || - cmdentry.u.bltin == typecmd)) { - savelocalvars = localvars; - localvars = 0; - mklocal(path - 5 /* PATH= */, 0); - temp_path = 1; - } else - temp_path = 0; - redirect(cmd->ncmd.redirect, mode); - - /* exec is a special builtin, but needs this list... */ - cmdenviron = varlist.list; - /* we must check 'readonly' flag for all builtins */ - listsetvar(varlist.list, - cmdentry.cmdtype == CMDSPLBLTIN ? 0 : VNOSET); - commandname = argv[0]; - /* initialize nextopt */ - argptr = argv + 1; - optptr = NULL; - /* and getopt */ -#ifndef __linux__ - optreset = 1; -#endif - optind = 1; - exitstatus = cmdentry.u.bltin(argc, argv); - } else { - e = exception; - exitstatus = e == EXINT ? SIGINT + 128 : - e == EXEXEC ? exerrno : 2; - } - handler = savehandler; - flushall(); - out1 = &output; - out2 = &errout; - freestdout(); - if (temp_path) { - poplocalvars(); - localvars = savelocalvars; - } - cmdenviron = NULL; - if (e != EXSHELLPROC) { - commandname = savecmdname; - if (flags & EV_EXIT) - exitshell(exitstatus); - } - if (e != -1) { - if ((e != EXERROR && e != EXEXEC) - || cmdentry.cmdtype == CMDSPLBLTIN) - exraise(e); - FORCEINTON; - } - if (cmdentry.u.bltin != execcmd) - popredir(); - if (flags == EV_BACKCMD) { - backcmd->buf = memout.buf; - backcmd->nleft = memout.nextc - memout.buf; - memout.buf = NULL; - } - break; - - default: -#ifdef DEBUG - trputs("normal command: "); trargs(argv); -#endif - clearredir(vforked); - redirect(cmd->ncmd.redirect, vforked ? REDIR_VFORK : 0); - if (!vforked) - for (sp = varlist.list ; sp ; sp = sp->next) - setvareq(sp->text, VEXPORT|VSTACK); - envp = environment(); - shellexec(argv, envp, path, cmdentry.u.index, vforked); - break; - } - goto out; - -parent: /* parent process gets here (if we forked) */ - if (mode == FORK_FG) { /* argument to fork */ - exitstatus = waitforjob(jp); - } else if (mode == FORK_NOJOB) { - backcmd->fd = pip[0]; - close(pip[1]); - backcmd->jp = jp; - } - FORCEINTON; - -out: - if (lastarg) - /* dsl: I think this is intended to be used to support - * '_' in 'vi' command mode during line editing... - * However I implemented that within libedit itself. - */ - setvar("_", lastarg, 0); - popstackmark(&smark); - - if (eflag && exitstatus && !(flags & EV_TESTED)) - exitshell(exitstatus); -} - - -/* - * Search for a command. This is called before we fork so that the - * location of the command will be available in the parent as well as - * the child. The check for "goodname" is an overly conservative - * check that the name will not be subject to expansion. - */ - -STATIC void -prehash(union node *n) -{ - struct cmdentry entry; - - if (n->type == NCMD && n->ncmd.args) - if (goodname(n->ncmd.args->narg.text)) - find_command(n->ncmd.args->narg.text, &entry, 0, - pathval()); -} - - - -/* - * Builtin commands. Builtin commands whose functions are closely - * tied to evaluation are implemented here. - */ - -/* - * No command given. - */ - -int -bltincmd(int argc, char **argv) -{ - /* - * Preserve exitstatus of a previous possible redirection - * as POSIX mandates - */ - return back_exitstatus; -} - - -/* - * Handle break and continue commands. Break, continue, and return are - * all handled by setting the evalskip flag. The evaluation routines - * above all check this flag, and if it is set they start skipping - * commands rather than executing them. The variable skipcount is - * the number of loops to break/continue, or the number of function - * levels to return. (The latter is always 1.) It should probably - * be an error to break out of more loops than exist, but it isn't - * in the standard shell so we don't make it one here. - */ - -int -breakcmd(int argc, char **argv) -{ - int n = argc > 1 ? number(argv[1]) : 1; - - if (n > loopnest) - n = loopnest; - if (n > 0) { - evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK; - skipcount = n; - } - return 0; -} - - -/* - * The return command. - */ - -int -returncmd(int argc, char **argv) -{ - int ret = argc > 1 ? number(argv[1]) : exitstatus; - - if (funcnest) { - evalskip = SKIPFUNC; - skipcount = 1; - return ret; - } - else { - /* Do what ksh does; skip the rest of the file */ - evalskip = SKIPFILE; - skipcount = 1; - return ret; - } -} - - -int -falsecmd(int argc, char **argv) -{ - return 1; -} - - -int -truecmd(int argc, char **argv) -{ - return 0; -} - - -int -execcmd(int argc, char **argv) -{ - if (argc > 1) { - struct strlist *sp; - - iflag = 0; /* exit on error */ - mflag = 0; - optschanged(); - for (sp = cmdenviron; sp; sp = sp->next) - setvareq(sp->text, VEXPORT|VSTACK); - shellexec(argv + 1, environment(), pathval(), 0, 0); - } - return 0; -} - -static int -conv_time(clock_t ticks, char *seconds, size_t l) -{ - static clock_t tpm = 0; - clock_t mins; - int i; - - mins = ticks / tpm; - snprintf(seconds, l, "%.4f", (ticks - mins * tpm) * 60.0 / tpm ); - - if (seconds[0] == '6' && seconds[1] == '0') { - /* 59.99995 got rounded up... */ - mins++; - strlcpy(seconds, "0.0", l); - return mins; - } - - /* suppress trailing zeros */ - i = strlen(seconds) - 1; - for (; seconds[i] == '0' && seconds[i - 1] != '.'; i--) - seconds[i] = 0; - return mins; -} - -int -timescmd(int argc, char **argv) -{ - struct tms tms; - int u, s, cu, cs; - char us[8], ss[8], cus[8], css[8]; - - nextopt(""); - - times(&tms); - - u = conv_time(tms.tms_utime, us, sizeof(us)); - s = conv_time(tms.tms_stime, ss, sizeof(ss)); - cu = conv_time(tms.tms_cutime, cus, sizeof(cus)); - cs = conv_time(tms.tms_cstime, css, sizeof(css)); - - outfmt(out1, "%dm%ss %dm%ss\n%dm%ss %dm%ss\n", - u, us, s, ss, cu, cus, cs, css); - - return 0; -} diff --git a/sh/eval.h b/sh/eval.h deleted file mode 100644 index 155bc44..0000000 --- a/sh/eval.h +++ /dev/null @@ -1,64 +0,0 @@ -/* $NetBSD: eval.h,v 1.14 2003/08/07 09:05:31 agc 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 - * Kenneth Almquist. - * - * 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. - * - * @(#)eval.h 8.2 (Berkeley) 5/4/95 - */ - -extern char *commandname; /* currently executing command */ -extern int exitstatus; /* exit status of last command */ -extern int back_exitstatus; /* exit status of backquoted command */ -extern struct strlist *cmdenviron; /* environment for builtin command */ - - -struct backcmd { /* result of evalbackcmd */ - int fd; /* file descriptor to read from */ - char *buf; /* buffer */ - int nleft; /* number of chars in buffer */ - struct job *jp; /* job structure for command */ -}; - -void evalstring(char *, int); -union node; /* BLETCH for ansi C */ -void evaltree(union node *, int); -void evalbackcmd(union node *, struct backcmd *); - -/* in_function returns nonzero if we are currently evaluating a function */ -#define in_function() funcnest -extern int funcnest; -extern int evalskip; - -/* reasons for skipping commands (see comment on breakcmd routine) */ -#define SKIPBREAK 1 -#define SKIPCONT 2 -#define SKIPFUNC 3 -#define SKIPFILE 4 diff --git a/sh/exec.c b/sh/exec.c deleted file mode 100644 index fe3613f..0000000 --- a/sh/exec.c +++ /dev/null @@ -1,1063 +0,0 @@ -/* $NetBSD: exec.c,v 1.37 2003/08/07 09:05:31 agc 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 - * Kenneth Almquist. - * - * 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[] = "@(#)exec.c 8.4 (Berkeley) 6/8/95"; -#else -__RCSID("$NetBSD: exec.c,v 1.37 2003/08/07 09:05:31 agc Exp $"); -#endif -#endif /* not lint */ - -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/wait.h> -#include <unistd.h> -#include <fcntl.h> -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> - -/* - * When commands are first encountered, they are entered in a hash table. - * This ensures that a full path search will not have to be done for them - * on each invocation. - * - * We should investigate converting to a linear search, even though that - * would make the command name "hash" a misnomer. - */ - -#include "shell.h" -#include "main.h" -#include "nodes.h" -#include "parser.h" -#include "redir.h" -#include "eval.h" -#include "exec.h" -#include "builtins.h" -#include "var.h" -#include "options.h" -#include "input.h" -#include "output.h" -#include "syntax.h" -#include "memalloc.h" -#include "error.h" -#include "init.h" -#include "mystring.h" -#include "show.h" -#include "jobs.h" -#include "alias.h" - - -#define CMDTABLESIZE 31 /* should be prime */ -#define ARB 1 /* actual size determined at run time */ - - - -struct tblentry { - struct tblentry *next; /* next entry in hash chain */ - union param param; /* definition of builtin function */ - short cmdtype; /* index identifying command */ - char rehash; /* if set, cd done since entry created */ - char cmdname[ARB]; /* name of command */ -}; - - -STATIC struct tblentry *cmdtable[CMDTABLESIZE]; -STATIC int builtinloc = -1; /* index in path of %builtin, or -1 */ -int exerrno = 0; /* Last exec error */ - - -STATIC void tryexec(char *, char **, char **, int); -STATIC void execinterp(char **, char **); -STATIC void printentry(struct tblentry *, int); -STATIC void clearcmdentry(int); -STATIC struct tblentry *cmdlookup(const char *, int); -STATIC void delete_cmd_entry(void); - - -extern char *const parsekwd[]; - -/* - * Exec a program. Never returns. If you change this routine, you may - * have to change the find_command routine as well. - */ - -void -shellexec(char **argv, char **envp, const char *path, int idx, int vforked) -{ - char *cmdname; - int e; - - if (strchr(argv[0], '/') != NULL) { - tryexec(argv[0], argv, envp, vforked); - e = errno; - } else { - e = ENOENT; - while ((cmdname = padvance(&path, argv[0])) != NULL) { - if (--idx < 0 && pathopt == NULL) { - tryexec(cmdname, argv, envp, vforked); - if (errno != ENOENT && errno != ENOTDIR) - e = errno; - } - stunalloc(cmdname); - } - } - - /* Map to POSIX errors */ - switch (e) { - case EACCES: - exerrno = 126; - break; - case ENOENT: - exerrno = 127; - break; - default: - exerrno = 2; - break; - } - TRACE(("shellexec failed for %s, errno %d, vforked %d, suppressint %d\n", - argv[0], e, vforked, suppressint )); - exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC)); - /* NOTREACHED */ -} - - -STATIC void -tryexec(char *cmd, char **argv, char **envp, int vforked) -{ - int e; -#ifndef BSD - char *p; -#endif - -#ifdef SYSV - do { - execve(cmd, argv, envp); - } while (errno == EINTR); -#else - execve(cmd, argv, envp); -#endif - e = errno; - if (e == ENOEXEC) { - if (vforked) { - /* We are currently vfork(2)ed, so raise an - * exception, and evalcommand will try again - * with a normal fork(2). - */ - exraise(EXSHELLPROC); - } - initshellproc(); - setinputfile(cmd, 0); - commandname = arg0 = savestr(argv[0]); -#if !defined(BSD) && !defined(__linux__) - pgetc(); pungetc(); /* fill up input buffer */ - p = parsenextc; - if (parsenleft > 2 && p[0] == '#' && p[1] == '!') { - argv[0] = cmd; - execinterp(argv, envp); - } -#endif - setparam(argv + 1); - exraise(EXSHELLPROC); - } - errno = e; -} - - -#if !defined(BSD) && !defined(__linux__) -/* - * Execute an interpreter introduced by "#!", for systems where this - * feature has not been built into the kernel. If the interpreter is - * the shell, return (effectively ignoring the "#!"). If the execution - * of the interpreter fails, exit. - * - * This code peeks inside the input buffer in order to avoid actually - * reading any input. It would benefit from a rewrite. - */ - -#define NEWARGS 5 - -STATIC void -execinterp(char **argv, char **envp) -{ - int n; - char *inp; - char *outp; - char c; - char *p; - char **ap; - char *newargs[NEWARGS]; - int i; - char **ap2; - char **new; - - n = parsenleft - 2; - inp = parsenextc + 2; - ap = newargs; - for (;;) { - while (--n >= 0 && (*inp == ' ' || *inp == '\t')) - inp++; - if (n < 0) - goto bad; - if ((c = *inp++) == '\n') - break; - if (ap == &newargs[NEWARGS]) -bad: error("Bad #! line"); - STARTSTACKSTR(outp); - do { - STPUTC(c, outp); - } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n'); - STPUTC('\0', outp); - n++, inp--; - *ap++ = grabstackstr(outp); - } - if (ap == newargs + 1) { /* if no args, maybe no exec is needed */ - p = newargs[0]; - for (;;) { - if (equal(p, "sh") || equal(p, "ash")) { - return; - } - while (*p != '/') { - if (*p == '\0') - goto break2; - p++; - } - p++; - } -break2:; - } - i = (char *)ap - (char *)newargs; /* size in bytes */ - if (i == 0) - error("Bad #! line"); - for (ap2 = argv ; *ap2++ != NULL ; ); - new = ckmalloc(i + ((char *)ap2 - (char *)argv)); - ap = newargs, ap2 = new; - while ((i -= sizeof (char **)) >= 0) - *ap2++ = *ap++; - ap = argv; - while (*ap2++ = *ap++); - shellexec(new, envp, pathval(), 0); - /* NOTREACHED */ -} -#endif - - - -/* - * Do a path search. The variable path (passed by reference) should be - * set to the start of the path before the first call; padvance will update - * this value as it proceeds. Successive calls to padvance will return - * the possible path expansions in sequence. If an option (indicated by - * a percent sign) appears in the path entry then the global variable - * pathopt will be set to point to it; otherwise pathopt will be set to - * NULL. - */ - -const char *pathopt; - -char * -padvance(const char **path, const char *name) -{ - const char *p; - char *q; - const char *start; - int len; - - if (*path == NULL) - return NULL; - start = *path; - for (p = start ; *p && *p != ':' && *p != '%' ; p++); - len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ - while (stackblocksize() < len) - growstackblock(); - q = stackblock(); - if (p != start) { - memcpy(q, start, p - start); - q += p - start; - *q++ = '/'; - } - strcpy(q, name); - pathopt = NULL; - if (*p == '%') { - pathopt = ++p; - while (*p && *p != ':') p++; - } - if (*p == ':') - *path = p + 1; - else - *path = NULL; - return stalloc(len); -} - - - -/*** Command hashing code ***/ - - -int -hashcmd(int argc, char **argv) -{ - struct tblentry **pp; - struct tblentry *cmdp; - int c; - int verbose; - struct cmdentry entry; - char *name; - - verbose = 0; - while ((c = nextopt("rv")) != '\0') { - if (c == 'r') { - clearcmdentry(0); - } else if (c == 'v') { - verbose++; - } - } - if (*argptr == NULL) { - for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { - for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { - if (verbose || cmdp->cmdtype == CMDNORMAL) - printentry(cmdp, verbose); - } - } - return 0; - } - while ((name = *argptr) != NULL) { - if ((cmdp = cmdlookup(name, 0)) != NULL - && (cmdp->cmdtype == CMDNORMAL - || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))) - delete_cmd_entry(); - find_command(name, &entry, DO_ERR, pathval()); - if (verbose) { - if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */ - cmdp = cmdlookup(name, 0); - printentry(cmdp, verbose); - } - flushall(); - } - argptr++; - } - return 0; -} - - -STATIC void -printentry(struct tblentry *cmdp, int verbose) -{ - int idx; - const char *path; - char *name; - - switch (cmdp->cmdtype) { - case CMDNORMAL: - idx = cmdp->param.index; - path = pathval(); - do { - name = padvance(&path, cmdp->cmdname); - stunalloc(name); - } while (--idx >= 0); - out1str(name); - break; - case CMDSPLBLTIN: - out1fmt("special builtin %s", cmdp->cmdname); - break; - case CMDBUILTIN: - out1fmt("builtin %s", cmdp->cmdname); - break; - case CMDFUNCTION: - out1fmt("function %s", cmdp->cmdname); - if (verbose) { - struct procstat ps; - INTOFF; - commandtext(&ps, cmdp->param.func); - INTON; - out1str("() { "); - out1str(ps.cmd); - out1str("; }"); - } - break; - default: - error("internal error: %s cmdtype %d", cmdp->cmdname, cmdp->cmdtype); - } - if (cmdp->rehash) - out1c('*'); - out1c('\n'); -} - - - -/* - * Resolve a command name. If you change this routine, you may have to - * change the shellexec routine as well. - */ - -void -find_command(char *name, struct cmdentry *entry, int act, const char *path) -{ - struct tblentry *cmdp, loc_cmd; - int idx; - int prev; - char *fullname; - struct stat statb; - int e; - int (*bltin)(int,char **); - - /* If name contains a slash, don't use PATH or hash table */ - if (strchr(name, '/') != NULL) { - if (act & DO_ABS) { - while (stat(name, &statb) < 0) { -#ifdef SYSV - if (errno == EINTR) - continue; -#endif - if (errno != ENOENT && errno != ENOTDIR) - e = errno; - entry->cmdtype = CMDUNKNOWN; - entry->u.index = -1; - return; - } - entry->cmdtype = CMDNORMAL; - entry->u.index = -1; - return; - } - entry->cmdtype = CMDNORMAL; - entry->u.index = 0; - return; - } - - if (path != pathval()) - act |= DO_ALTPATH; - - if (act & DO_ALTPATH && strstr(path, "%builtin") != NULL) - act |= DO_ALTBLTIN; - - /* If name is in the table, check answer will be ok */ - if ((cmdp = cmdlookup(name, 0)) != NULL) { - do { - switch (cmdp->cmdtype) { - case CMDNORMAL: - if (act & DO_ALTPATH) { - cmdp = NULL; - continue; - } - break; - case CMDFUNCTION: - if (act & DO_NOFUNC) { - cmdp = NULL; - continue; - } - break; - case CMDBUILTIN: - if ((act & DO_ALTBLTIN) || builtinloc >= 0) { - cmdp = NULL; - continue; - } - break; - } - /* if not invalidated by cd, we're done */ - if (cmdp->rehash == 0) - goto success; - } while (0); - } - - /* If %builtin not in path, check for builtin next */ - if ((act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc < 0) && - (bltin = find_builtin(name)) != 0) - goto builtin_success; - - /* We have to search path. */ - prev = -1; /* where to start */ - if (cmdp) { /* doing a rehash */ - if (cmdp->cmdtype == CMDBUILTIN) - prev = builtinloc; - else - prev = cmdp->param.index; - } - - e = ENOENT; - idx = -1; -loop: - while ((fullname = padvance(&path, name)) != NULL) { - stunalloc(fullname); - idx++; - if (pathopt) { - if (prefix("builtin", pathopt)) { - if ((bltin = find_builtin(name)) == 0) - goto loop; - goto builtin_success; - } else if (prefix("func", pathopt)) { - /* handled below */ - } else { - /* ignore unimplemented options */ - goto loop; - } - } - /* if rehash, don't redo absolute path names */ - if (fullname[0] == '/' && idx <= prev) { - if (idx < prev) - goto loop; - TRACE(("searchexec \"%s\": no change\n", name)); - goto success; - } - while (stat(fullname, &statb) < 0) { -#ifdef SYSV - if (errno == EINTR) - continue; -#endif - if (errno != ENOENT && errno != ENOTDIR) - e = errno; - goto loop; - } - e = EACCES; /* if we fail, this will be the error */ - if (!S_ISREG(statb.st_mode)) - goto loop; - if (pathopt) { /* this is a %func directory */ - if (act & DO_NOFUNC) - goto loop; - stalloc(strlen(fullname) + 1); - readcmdfile(fullname); - if ((cmdp = cmdlookup(name, 0)) == NULL || - cmdp->cmdtype != CMDFUNCTION) - error("%s not defined in %s", name, fullname); - stunalloc(fullname); - goto success; - } -#ifdef notdef - /* XXX this code stops root executing stuff, and is buggy - if you need a group from the group list. */ - if (statb.st_uid == geteuid()) { - if ((statb.st_mode & 0100) == 0) - goto loop; - } else if (statb.st_gid == getegid()) { - if ((statb.st_mode & 010) == 0) - goto loop; - } else { - if ((statb.st_mode & 01) == 0) - goto loop; - } -#endif - TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname)); - INTOFF; - if (act & DO_ALTPATH) { - stalloc(strlen(fullname) + 1); - cmdp = &loc_cmd; - } else - cmdp = cmdlookup(name, 1); - cmdp->cmdtype = CMDNORMAL; - cmdp->param.index = idx; - INTON; - goto success; - } - - /* We failed. If there was an entry for this command, delete it */ - if (cmdp) - delete_cmd_entry(); - if (act & DO_ERR) - outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC)); - entry->cmdtype = CMDUNKNOWN; - return; - -builtin_success: - INTOFF; - if (act & DO_ALTPATH) - cmdp = &loc_cmd; - else - cmdp = cmdlookup(name, 1); - if (cmdp->cmdtype == CMDFUNCTION) - /* DO_NOFUNC must have been set */ - cmdp = &loc_cmd; - cmdp->cmdtype = CMDBUILTIN; - cmdp->param.bltin = bltin; - INTON; -success: - cmdp->rehash = 0; - entry->cmdtype = cmdp->cmdtype; - entry->u = cmdp->param; -} - - - -/* - * Search the table of builtin commands. - */ - -int -(*find_builtin(name))(int, char **) - char *name; -{ - const struct builtincmd *bp; - - for (bp = builtincmd ; bp->name ; bp++) { - if (*bp->name == *name && equal(bp->name, name)) - return bp->builtin; - } - return 0; -} - -int -(*find_splbltin(name))(int, char **) - char *name; -{ - const struct builtincmd *bp; - - for (bp = splbltincmd ; bp->name ; bp++) { - if (*bp->name == *name && equal(bp->name, name)) - return bp->builtin; - } - return 0; -} - -/* - * At shell startup put special builtins into hash table. - * ensures they are executed first (see posix). - * We stop functions being added with the same name - * (as they are impossible to call) - */ - -void -hash_special_builtins(void) -{ - const struct builtincmd *bp; - struct tblentry *cmdp; - - for (bp = splbltincmd ; bp->name ; bp++) { - cmdp = cmdlookup(bp->name, 1); - cmdp->cmdtype = CMDSPLBLTIN; - cmdp->param.bltin = bp->builtin; - } -} - - - -/* - * Called when a cd is done. Marks all commands so the next time they - * are executed they will be rehashed. - */ - -void -hashcd(void) -{ - struct tblentry **pp; - struct tblentry *cmdp; - - for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { - for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { - if (cmdp->cmdtype == CMDNORMAL - || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)) - cmdp->rehash = 1; - } - } -} - - - -/* - * Fix command hash table when PATH changed. - * Called before PATH is changed. The argument is the new value of PATH; - * pathval() still returns the old value at this point. - * Called with interrupts off. - */ - -void -changepath(const char *newval) -{ - const char *old, *new; - int idx; - int firstchange; - int bltin; - - old = pathval(); - new = newval; - firstchange = 9999; /* assume no change */ - idx = 0; - bltin = -1; - for (;;) { - if (*old != *new) { - firstchange = idx; - if ((*old == '\0' && *new == ':') - || (*old == ':' && *new == '\0')) - firstchange++; - old = new; /* ignore subsequent differences */ - } - if (*new == '\0') - break; - if (*new == '%' && bltin < 0 && prefix("builtin", new + 1)) - bltin = idx; - if (*new == ':') { - idx++; - } - new++, old++; - } - if (builtinloc < 0 && bltin >= 0) - builtinloc = bltin; /* zap builtins */ - if (builtinloc >= 0 && bltin < 0) - firstchange = 0; - clearcmdentry(firstchange); - builtinloc = bltin; -} - - -/* - * Clear out command entries. The argument specifies the first entry in - * PATH which has changed. - */ - -STATIC void -clearcmdentry(int firstchange) -{ - struct tblentry **tblp; - struct tblentry **pp; - struct tblentry *cmdp; - - INTOFF; - for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { - pp = tblp; - while ((cmdp = *pp) != NULL) { - if ((cmdp->cmdtype == CMDNORMAL && - cmdp->param.index >= firstchange) - || (cmdp->cmdtype == CMDBUILTIN && - builtinloc >= firstchange)) { - *pp = cmdp->next; - ckfree(cmdp); - } else { - pp = &cmdp->next; - } - } - } - INTON; -} - - -/* - * Delete all functions. - */ - -#ifdef mkinit -MKINIT void deletefuncs(void); -MKINIT void hash_special_builtins(void); - -INIT { - hash_special_builtins(); -} - -SHELLPROC { - deletefuncs(); -} -#endif - -void -deletefuncs(void) -{ - struct tblentry **tblp; - struct tblentry **pp; - struct tblentry *cmdp; - - INTOFF; - for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { - pp = tblp; - while ((cmdp = *pp) != NULL) { - if (cmdp->cmdtype == CMDFUNCTION) { - *pp = cmdp->next; - freefunc(cmdp->param.func); - ckfree(cmdp); - } else { - pp = &cmdp->next; - } - } - } - INTON; -} - - - -/* - * Locate a command in the command hash table. If "add" is nonzero, - * add the command to the table if it is not already present. The - * variable "lastcmdentry" is set to point to the address of the link - * pointing to the entry, so that delete_cmd_entry can delete the - * entry. - */ - -struct tblentry **lastcmdentry; - - -STATIC struct tblentry * -cmdlookup(const char *name, int add) -{ - int hashval; - const char *p; - struct tblentry *cmdp; - struct tblentry **pp; - - p = name; - hashval = *p << 4; - while (*p) - hashval += *p++; - hashval &= 0x7FFF; - pp = &cmdtable[hashval % CMDTABLESIZE]; - for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { - if (equal(cmdp->cmdname, name)) - break; - pp = &cmdp->next; - } - if (add && cmdp == NULL) { - INTOFF; - cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB - + strlen(name) + 1); - cmdp->next = NULL; - cmdp->cmdtype = CMDUNKNOWN; - cmdp->rehash = 0; - strcpy(cmdp->cmdname, name); - INTON; - } - lastcmdentry = pp; - return cmdp; -} - -/* - * Delete the command entry returned on the last lookup. - */ - -STATIC void -delete_cmd_entry(void) -{ - struct tblentry *cmdp; - - INTOFF; - cmdp = *lastcmdentry; - *lastcmdentry = cmdp->next; - ckfree(cmdp); - INTON; -} - - - -#ifdef notdef -void -getcmdentry(char *name, struct cmdentry *entry) -{ - struct tblentry *cmdp = cmdlookup(name, 0); - - if (cmdp) { - entry->u = cmdp->param; - entry->cmdtype = cmdp->cmdtype; - } else { - entry->cmdtype = CMDUNKNOWN; - entry->u.index = 0; - } -} -#endif - - -/* - * Add a new command entry, replacing any existing command entry for - * the same name - except special builtins. - */ - -STATIC void -addcmdentry(char *name, struct cmdentry *entry) -{ - struct tblentry *cmdp; - - INTOFF; - cmdp = cmdlookup(name, 1); - if (cmdp->cmdtype != CMDSPLBLTIN) { - if (cmdp->cmdtype == CMDFUNCTION) { - freefunc(cmdp->param.func); - } - cmdp->cmdtype = entry->cmdtype; - cmdp->param = entry->u; - } - INTON; -} - - -/* - * Define a shell function. - */ - -void -defun(char *name, union node *func) -{ - struct cmdentry entry; - - INTOFF; - entry.cmdtype = CMDFUNCTION; - entry.u.func = copyfunc(func); - addcmdentry(name, &entry); - INTON; -} - - -/* - * Delete a function if it exists. - */ - -int -unsetfunc(char *name) -{ - struct tblentry *cmdp; - - if ((cmdp = cmdlookup(name, 0)) != NULL && - cmdp->cmdtype == CMDFUNCTION) { - freefunc(cmdp->param.func); - delete_cmd_entry(); - return (0); - } - return (1); -} - -/* - * Locate and print what a word is... - * also used for 'command -[v|V]' - */ - -int -typecmd(int argc, char **argv) -{ - struct cmdentry entry; - struct tblentry *cmdp; - char * const *pp; - struct alias *ap; - int err = 0; - char *arg; - int c; - int V_flag = 0; - int v_flag = 0; - int p_flag = 0; - - while ((c = nextopt("vVp")) != 0) { - switch (c) { - case 'v': v_flag = 1; break; - case 'V': V_flag = 1; break; - case 'p': p_flag = 1; break; - } - } - - if (p_flag && (v_flag || V_flag)) - error("cannot specify -p with -v or -V"); - - while ((arg = *argptr++)) { - if (!v_flag) - out1str(arg); - /* First look at the keywords */ - for (pp = parsekwd; *pp; pp++) - if (**pp == *arg && equal(*pp, arg)) - break; - - if (*pp) { - if (v_flag) - err = 1; - else - out1str(" is a shell keyword\n"); - continue; - } - - /* Then look at the aliases */ - if ((ap = lookupalias(arg, 1)) != NULL) { - if (!v_flag) - out1fmt(" is an alias for \n"); - out1fmt("%s\n", ap->val); - continue; - } - - /* Then check if it is a tracked alias */ - if ((cmdp = cmdlookup(arg, 0)) != NULL) { - entry.cmdtype = cmdp->cmdtype; - entry.u = cmdp->param; - } else { - /* Finally use brute force */ - find_command(arg, &entry, DO_ABS, pathval()); - } - - switch (entry.cmdtype) { - case CMDNORMAL: { - if (strchr(arg, '/') == NULL) { - const char *path = pathval(); - char *name; - int j = entry.u.index; - do { - name = padvance(&path, arg); - stunalloc(name); - } while (--j >= 0); - if (!v_flag) - out1fmt(" is%s ", - cmdp ? " a tracked alias for" : ""); - out1fmt("%s\n", name); - } else { - if (access(arg, X_OK) == 0) { - if (!v_flag) - out1fmt(" is "); - out1fmt("%s\n", arg); - } else { - if (!v_flag) - out1fmt(": %s\n", - strerror(errno)); - else - err = 126; - } - } - break; - } - case CMDFUNCTION: - if (!v_flag) - out1str(" is a shell function\n"); - else - out1fmt("%s\n", arg); - break; - - case CMDBUILTIN: - if (!v_flag) - out1str(" is a shell builtin\n"); - else - out1fmt("%s\n", arg); - break; - - case CMDSPLBLTIN: - if (!v_flag) - out1str(" is a special shell builtin\n"); - else - out1fmt("%s\n", arg); - break; - - default: - if (!v_flag) - out1str(": not found\n"); - err = 127; - break; - } - } - return err; -} diff --git a/sh/exec.h b/sh/exec.h deleted file mode 100644 index 26fd09c..0000000 --- a/sh/exec.h +++ /dev/null @@ -1,79 +0,0 @@ -/* $NetBSD: exec.h,v 1.21 2003/08/07 09:05:31 agc 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 - * Kenneth Almquist. - * - * 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. - * - * @(#)exec.h 8.3 (Berkeley) 6/8/95 - */ - -/* values of cmdtype */ -#define CMDUNKNOWN -1 /* no entry in table for command */ -#define CMDNORMAL 0 /* command is an executable program */ -#define CMDFUNCTION 1 /* command is a shell function */ -#define CMDBUILTIN 2 /* command is a shell builtin */ -#define CMDSPLBLTIN 3 /* command is a special shell builtin */ - - -struct cmdentry { - int cmdtype; - union param { - int index; - int (*bltin)(int, char**); - union node *func; - } u; -}; - - -/* action to find_command() */ -#define DO_ERR 0x01 /* prints errors */ -#define DO_ABS 0x02 /* checks absolute paths */ -#define DO_NOFUNC 0x04 /* don't return shell functions, for command */ -#define DO_ALTPATH 0x08 /* using alternate path */ -#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */ - -extern const char *pathopt; /* set by padvance */ - -void shellexec(char **, char **, const char *, int, int) - __attribute__((__noreturn__)); -char *padvance(const char **, const char *); -int hashcmd(int, char **); -void find_command(char *, struct cmdentry *, int, const char *); -int (*find_builtin(char *))(int, char **); -int (*find_splbltin(char *))(int, char **); -void hashcd(void); -void changepath(const char *); -void deletefuncs(void); -void getcmdentry(char *, struct cmdentry *); -void addcmdentry(char *, struct cmdentry *); -void defun(char *, union node *); -int unsetfunc(char *); -int typecmd(int, char **); -void hash_special_builtins(void); diff --git a/sh/expand.c b/sh/expand.c deleted file mode 100644 index d3462fc..0000000 --- a/sh/expand.c +++ /dev/null @@ -1,1559 +0,0 @@ -/* $NetBSD: expand.c,v 1.68.2.2 2005/04/07 11:37:39 tron 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 - * Kenneth Almquist. - * - * 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[] = "@(#)expand.c 8.5 (Berkeley) 5/15/95"; -#else -__RCSID("$NetBSD: expand.c,v 1.68.2.2 2005/04/07 11:37:39 tron Exp $"); -#endif -#endif /* not lint */ - -#include <sys/types.h> -#include <sys/time.h> -#include <sys/stat.h> -#include <errno.h> -#include <dirent.h> -#include <unistd.h> -#include <stdlib.h> -#include <stdio.h> - -/* - * Routines to expand arguments to commands. We have to deal with - * backquotes, shell variables, and file metacharacters. - */ - -#include "shell.h" -#include "main.h" -#include "nodes.h" -#include "eval.h" -#include "expand.h" -#include "syntax.h" -#include "parser.h" -#include "jobs.h" -#include "options.h" -#include "var.h" -#include "input.h" -#include "output.h" -#include "memalloc.h" -#include "error.h" -#include "mystring.h" -#include "show.h" - -/* - * Structure specifying which parts of the string should be searched - * for IFS characters. - */ - -struct ifsregion { - struct ifsregion *next; /* next region in list */ - int begoff; /* offset of start of region */ - int endoff; /* offset of end of region */ - int inquotes; /* search for nul bytes only */ -}; - - -char *expdest; /* output of current string */ -struct nodelist *argbackq; /* list of back quote expressions */ -struct ifsregion ifsfirst; /* first struct in list of ifs regions */ -struct ifsregion *ifslastp; /* last struct in list */ -struct arglist exparg; /* holds expanded arg list */ - -STATIC void argstr(char *, int); -STATIC char *exptilde(char *, int); -STATIC void expbackq(union node *, int, int); -STATIC int subevalvar(char *, char *, int, int, int, int); -STATIC char *evalvar(char *, int); -STATIC int varisset(char *, int); -STATIC void varvalue(char *, int, int, int); -STATIC void recordregion(int, int, int); -STATIC void removerecordregions(int); -STATIC void ifsbreakup(char *, struct arglist *); -STATIC void ifsfree(void); -STATIC void expandmeta(struct strlist *, int); -STATIC void expmeta(char *, char *); -STATIC void addfname(char *); -STATIC struct strlist *expsort(struct strlist *); -STATIC struct strlist *msort(struct strlist *, int); -STATIC int pmatch(char *, char *, int); -STATIC char *cvtnum(int, char *); - -/* - * Expand shell variables and backquotes inside a here document. - */ - -void -expandhere(union node *arg, int fd) -{ - herefd = fd; - expandarg(arg, (struct arglist *)NULL, 0); - xwrite(fd, stackblock(), expdest - stackblock()); -} - - -/* - * Perform variable substitution and command substitution on an argument, - * placing the resulting list of arguments in arglist. If EXP_FULL is true, - * perform splitting and file name expansion. When arglist is NULL, perform - * here document expansion. - */ - -void -expandarg(union node *arg, struct arglist *arglist, int flag) -{ - struct strlist *sp; - char *p; - - argbackq = arg->narg.backquote; - STARTSTACKSTR(expdest); - ifsfirst.next = NULL; - ifslastp = NULL; - argstr(arg->narg.text, flag); - if (arglist == NULL) { - return; /* here document expanded */ - } - STPUTC('\0', expdest); - p = grabstackstr(expdest); - exparg.lastp = &exparg.list; - /* - * TODO - EXP_REDIR - */ - if (flag & EXP_FULL) { - ifsbreakup(p, &exparg); - *exparg.lastp = NULL; - exparg.lastp = &exparg.list; - expandmeta(exparg.list, flag); - } else { - if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ - rmescapes(p); - sp = (struct strlist *)stalloc(sizeof (struct strlist)); - sp->text = p; - *exparg.lastp = sp; - exparg.lastp = &sp->next; - } - ifsfree(); - *exparg.lastp = NULL; - if (exparg.list) { - *arglist->lastp = exparg.list; - arglist->lastp = exparg.lastp; - } -} - - - -/* - * Perform variable and command substitution. - * If EXP_FULL is set, output CTLESC characters to allow for further processing. - * Otherwise treat $@ like $* since no splitting will be performed. - */ - -STATIC void -argstr(char *p, int flag) -{ - char c; - int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */ - int firsteq = 1; - const char *ifs = 0; - int ifs_split = EXP_IFS_SPLIT; - - if (flag & EXP_IFS_SPLIT) - ifs = ifsset() ? ifsval() : " \t\n"; - - if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) - p = exptilde(p, flag); - for (;;) { - switch (c = *p++) { - case '\0': - case CTLENDVAR: /* end of expanding yyy in ${xxx-yyy} */ - return; - case CTLQUOTEMARK: - /* "$@" syntax adherence hack */ - if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=') - break; - if ((flag & EXP_FULL) != 0) - STPUTC(c, expdest); - ifs_split = 0; - break; - case CTLQUOTEEND: - ifs_split = EXP_IFS_SPLIT; - break; - case CTLESC: - if (quotes) - STPUTC(c, expdest); - c = *p++; - STPUTC(c, expdest); - break; - case CTLVAR: - p = evalvar(p, (flag & ~EXP_IFS_SPLIT) | (flag & ifs_split)); - break; - case CTLBACKQ: - case CTLBACKQ|CTLQUOTE: - expbackq(argbackq->n, c & CTLQUOTE, flag); - argbackq = argbackq->next; - break; - case CTLENDARI: - expari(flag); - break; - case ':': - case '=': - /* - * sort of a hack - expand tildes in variable - * assignments (after the first '=' and after ':'s). - */ - STPUTC(c, expdest); - if (flag & EXP_VARTILDE && *p == '~') { - if (c == '=') { - if (firsteq) - firsteq = 0; - else - break; - } - p = exptilde(p, flag); - } - break; - default: - STPUTC(c, expdest); - if (flag & EXP_IFS_SPLIT & ifs_split && strchr(ifs, c) != NULL) { - /* We need to get the output split here... */ - recordregion(expdest - stackblock() - 1, - expdest - stackblock(), 0); - } - break; - } - } -} - -STATIC char * -exptilde(char *p, int flag) -{ - char c, *startp = p; - const char *home; - int quotes = flag & (EXP_FULL | EXP_CASE); - - while ((c = *p) != '\0') { - switch(c) { - case CTLESC: - return (startp); - case CTLQUOTEMARK: - return (startp); - case ':': - if (flag & EXP_VARTILDE) - goto done; - break; - case '/': - goto done; - } - p++; - } -done: - *p = '\0'; - if (*(startp+1) == '\0') { - if ((home = lookupvar("HOME")) == NULL) - goto lose; - } else - goto lose; - if (*home == '\0') - goto lose; - *p = c; - while ((c = *home++) != '\0') { - if (quotes && SQSYNTAX[(int)c] == CCTL) - STPUTC(CTLESC, expdest); - STPUTC(c, expdest); - } - return (p); -lose: - *p = c; - return (startp); -} - - -STATIC void -removerecordregions(int endoff) -{ - if (ifslastp == NULL) - return; - - if (ifsfirst.endoff > endoff) { - while (ifsfirst.next != NULL) { - struct ifsregion *ifsp; - INTOFF; - ifsp = ifsfirst.next->next; - ckfree(ifsfirst.next); - ifsfirst.next = ifsp; - INTON; - } - if (ifsfirst.begoff > endoff) - ifslastp = NULL; - else { - ifslastp = &ifsfirst; - ifsfirst.endoff = endoff; - } - return; - } - - ifslastp = &ifsfirst; - while (ifslastp->next && ifslastp->next->begoff < endoff) - ifslastp=ifslastp->next; - while (ifslastp->next != NULL) { - struct ifsregion *ifsp; - INTOFF; - ifsp = ifslastp->next->next; - ckfree(ifslastp->next); - ifslastp->next = ifsp; - INTON; - } - if (ifslastp->endoff > endoff) - ifslastp->endoff = endoff; -} - - -/* - * Expand arithmetic expression. Backup to start of expression, - * evaluate, place result in (backed up) result, adjust string position. - */ -void -expari(int flag) -{ - char *p, *start; - int result; - int begoff; - int quotes = flag & (EXP_FULL | EXP_CASE); - int quoted; - - /* ifsfree(); */ - - /* - * This routine is slightly over-complicated for - * efficiency. First we make sure there is - * enough space for the result, which may be bigger - * than the expression if we add exponentation. Next we - * scan backwards looking for the start of arithmetic. If the - * next previous character is a CTLESC character, then we - * have to rescan starting from the beginning since CTLESC - * characters have to be processed left to right. - */ -#if INT_MAX / 1000000000 >= 10 || INT_MIN / 1000000000 <= -10 -#error "integers with more than 10 digits are not supported" -#endif - CHECKSTRSPACE(12 - 2, expdest); - USTPUTC('\0', expdest); - start = stackblock(); - p = expdest - 1; - while (*p != CTLARI && p >= start) - --p; - if (*p != CTLARI) - error("missing CTLARI (shouldn't happen)"); - if (p > start && *(p-1) == CTLESC) - for (p = start; *p != CTLARI; p++) - if (*p == CTLESC) - p++; - - if (p[1] == '"') - quoted=1; - else - quoted=0; - begoff = p - start; - removerecordregions(begoff); - if (quotes) - rmescapes(p+2); - result = arith(p+2); - fmtstr(p, 12, "%d", result); - - while (*p++) - ; - - if (quoted == 0) - recordregion(begoff, p - 1 - start, 0); - result = expdest - p + 1; - STADJUST(-result, expdest); -} - - -/* - * Expand stuff in backwards quotes. - */ - -STATIC void -expbackq(union node *cmd, int quoted, int flag) -{ - struct backcmd in; - int i; - char buf[128]; - char *p; - char *dest = expdest; - struct ifsregion saveifs, *savelastp; - struct nodelist *saveargbackq; - char lastc; - int startloc = dest - stackblock(); - char const *syntax = quoted? DQSYNTAX : BASESYNTAX; - int saveherefd; - int quotes = flag & (EXP_FULL | EXP_CASE); - - INTOFF; - saveifs = ifsfirst; - savelastp = ifslastp; - saveargbackq = argbackq; - saveherefd = herefd; - herefd = -1; - p = grabstackstr(dest); - evalbackcmd(cmd, &in); - ungrabstackstr(p, dest); - ifsfirst = saveifs; - ifslastp = savelastp; - argbackq = saveargbackq; - herefd = saveherefd; - - p = in.buf; - lastc = '\0'; - for (;;) { - if (--in.nleft < 0) { - if (in.fd < 0) - break; - while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR); - TRACE(("expbackq: read returns %d\n", i)); - if (i <= 0) - break; - p = buf; - in.nleft = i - 1; - } - lastc = *p++; - if (lastc != '\0') { - if (quotes && syntax[(int)lastc] == CCTL) - STPUTC(CTLESC, dest); - STPUTC(lastc, dest); - } - } - - /* Eat all trailing newlines */ - p = stackblock() + startloc; - while (dest > p && dest[-1] == '\n') - STUNPUTC(dest); - - if (in.fd >= 0) - close(in.fd); - if (in.buf) - ckfree(in.buf); - if (in.jp) - back_exitstatus = waitforjob(in.jp); - if (quoted == 0) - recordregion(startloc, dest - stackblock(), 0); - TRACE(("evalbackq: size=%d: \"%.*s\"\n", - (dest - stackblock()) - startloc, - (dest - stackblock()) - startloc, - stackblock() + startloc)); - expdest = dest; - INTON; -} - - - -STATIC int -subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags) -{ - char *startp; - char *loc = NULL; - char *q; - int c = 0; - int saveherefd = herefd; - struct nodelist *saveargbackq = argbackq; - int amount; - - herefd = -1; - argstr(p, 0); - STACKSTRNUL(expdest); - herefd = saveherefd; - argbackq = saveargbackq; - startp = stackblock() + startloc; - if (str == NULL) - str = stackblock() + strloc; - - switch (subtype) { - case VSASSIGN: - setvar(str, startp, 0); - amount = startp - expdest; - STADJUST(amount, expdest); - varflags &= ~VSNUL; - if (c != 0) - *loc = c; - return 1; - - case VSQUESTION: - if (*p != CTLENDVAR) { - outfmt(&errout, "%s\n", startp); - error((char *)NULL); - } - error("%.*s: parameter %snot set", p - str - 1, - str, (varflags & VSNUL) ? "null or " - : nullstr); - /* NOTREACHED */ - - case VSTRIMLEFT: - for (loc = startp; loc < str; loc++) { - c = *loc; - *loc = '\0'; - if (patmatch(str, startp, varflags & VSQUOTE)) - goto recordleft; - *loc = c; - if ((varflags & VSQUOTE) && *loc == CTLESC) - loc++; - } - return 0; - - case VSTRIMLEFTMAX: - for (loc = str - 1; loc >= startp;) { - c = *loc; - *loc = '\0'; - if (patmatch(str, startp, varflags & VSQUOTE)) - goto recordleft; - *loc = c; - loc--; - if ((varflags & VSQUOTE) && loc > startp && - *(loc - 1) == CTLESC) { - for (q = startp; q < loc; q++) - if (*q == CTLESC) - q++; - if (q > loc) - loc--; - } - } - return 0; - - case VSTRIMRIGHT: - for (loc = str - 1; loc >= startp;) { - if (patmatch(str, loc, varflags & VSQUOTE)) - goto recordright; - loc--; - if ((varflags & VSQUOTE) && loc > startp && - *(loc - 1) == CTLESC) { - for (q = startp; q < loc; q++) - if (*q == CTLESC) - q++; - if (q > loc) - loc--; - } - } - return 0; - - case VSTRIMRIGHTMAX: - for (loc = startp; loc < str - 1; loc++) { - if (patmatch(str, loc, varflags & VSQUOTE)) - goto recordright; - if ((varflags & VSQUOTE) && *loc == CTLESC) - loc++; - } - return 0; - - default: - abort(); - } - -recordleft: - *loc = c; - amount = ((str - 1) - (loc - startp)) - expdest; - STADJUST(amount, expdest); - while (loc != str - 1) - *startp++ = *loc++; - return 1; - -recordright: - amount = loc - expdest; - STADJUST(amount, expdest); - STPUTC('\0', expdest); - STADJUST(-1, expdest); - return 1; -} - - -/* - * Expand a variable, and return a pointer to the next character in the - * input string. - */ - -STATIC char * -evalvar(char *p, int flag) -{ - int subtype; - int varflags; - char *var; - char *val; - int patloc; - int c; - int set; - int special; - int startloc; - int varlen; - int apply_ifs; - int quotes = flag & (EXP_FULL | EXP_CASE); - - varflags = (unsigned char)*p++; - subtype = varflags & VSTYPE; - var = p; - special = !is_name(*p); - p = strchr(p, '=') + 1; - -again: /* jump here after setting a variable with ${var=text} */ - if (special) { - set = varisset(var, varflags & VSNUL); - val = NULL; - } else { - val = lookupvar(var); - if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) { - val = NULL; - set = 0; - } else - set = 1; - } - - varlen = 0; - startloc = expdest - stackblock(); - - if (!set && uflag) { - switch (subtype) { - case VSNORMAL: - case VSTRIMLEFT: - case VSTRIMLEFTMAX: - case VSTRIMRIGHT: - case VSTRIMRIGHTMAX: - case VSLENGTH: - error("%.*s: parameter not set", p - var - 1, var); - /* NOTREACHED */ - } - } - - if (set && subtype != VSPLUS) { - /* insert the value of the variable */ - if (special) { - varvalue(var, varflags & VSQUOTE, subtype, flag); - if (subtype == VSLENGTH) { - varlen = expdest - stackblock() - startloc; - STADJUST(-varlen, expdest); - } - } else { - char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX - : BASESYNTAX; - - if (subtype == VSLENGTH) { - for (;*val; val++) - varlen++; - } else { - while (*val) { - if (quotes && syntax[(int)*val] == CCTL) - STPUTC(CTLESC, expdest); - STPUTC(*val++, expdest); - } - - } - } - } - - - apply_ifs = ((varflags & VSQUOTE) == 0 || - (*var == '@' && shellparam.nparam != 1)); - - switch (subtype) { - case VSLENGTH: - expdest = cvtnum(varlen, expdest); - break; - - case VSNORMAL: - break; - - case VSPLUS: - set = !set; - /* FALLTHROUGH */ - case VSMINUS: - if (!set) { - argstr(p, flag | (apply_ifs ? EXP_IFS_SPLIT : 0)); - /* - * ${x-a b c} doesn't get split, but removing the - * 'apply_ifs = 0' apparantly breaks ${1+"$@"}.. - * ${x-'a b' c} should generate 2 args. - */ - /* We should have marked stuff already */ - apply_ifs = 0; - } - break; - - case VSTRIMLEFT: - case VSTRIMLEFTMAX: - case VSTRIMRIGHT: - case VSTRIMRIGHTMAX: - if (!set) - break; - /* - * Terminate the string and start recording the pattern - * right after it - */ - STPUTC('\0', expdest); - patloc = expdest - stackblock(); - if (subevalvar(p, NULL, patloc, subtype, - startloc, varflags) == 0) { - int amount = (expdest - stackblock() - patloc) + 1; - STADJUST(-amount, expdest); - } - /* Remove any recorded regions beyond start of variable */ - removerecordregions(startloc); - apply_ifs = 1; - break; - - case VSASSIGN: - case VSQUESTION: - if (set) - break; - if (subevalvar(p, var, 0, subtype, startloc, varflags)) { - varflags &= ~VSNUL; - /* - * Remove any recorded regions beyond - * start of variable - */ - removerecordregions(startloc); - goto again; - } - apply_ifs = 0; - break; - - default: - abort(); - } - - if (apply_ifs) - recordregion(startloc, expdest - stackblock(), - varflags & VSQUOTE); - - if (subtype != VSNORMAL) { /* skip to end of alternative */ - int nesting = 1; - for (;;) { - if ((c = *p++) == CTLESC) - p++; - else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { - if (set) - argbackq = argbackq->next; - } else if (c == CTLVAR) { - if ((*p++ & VSTYPE) != VSNORMAL) - nesting++; - } else if (c == CTLENDVAR) { - if (--nesting == 0) - break; - } - } - } - return p; -} - - - -/* - * Test whether a specialized variable is set. - */ - -STATIC int -varisset(char *name, int nulok) -{ - if (*name == '!') - return backgndpid != -1; - else if (*name == '@' || *name == '*') { - if (*shellparam.p == NULL) - return 0; - - if (nulok) { - char **av; - - for (av = shellparam.p; *av; av++) - if (**av != '\0') - return 1; - return 0; - } - } else if (is_digit(*name)) { - char *ap; - int num = atoi(name); - - if (num > shellparam.nparam) - return 0; - - if (num == 0) - ap = arg0; - else - ap = shellparam.p[num - 1]; - - if (nulok && (ap == NULL || *ap == '\0')) - return 0; - } - return 1; -} - - - -/* - * Add the value of a specialized variable to the stack string. - */ - -STATIC void -varvalue(char *name, int quoted, int subtype, int flag) -{ - int num; - char *p; - int i; - char sep; - char **ap; - char const *syntax; - -#define STRTODEST(p) \ - do {\ - if (flag & (EXP_FULL | EXP_CASE) && subtype != VSLENGTH) { \ - syntax = quoted? DQSYNTAX : BASESYNTAX; \ - while (*p) { \ - if (syntax[(int)*p] == CCTL) \ - STPUTC(CTLESC, expdest); \ - STPUTC(*p++, expdest); \ - } \ - } else \ - while (*p) \ - STPUTC(*p++, expdest); \ - } while (0) - - - switch (*name) { - case '$': - num = rootpid; - goto numvar; - case '?': - num = exitstatus; - goto numvar; - case '#': - num = shellparam.nparam; - goto numvar; - case '!': - num = backgndpid; -numvar: - expdest = cvtnum(num, expdest); - break; - case '-': - for (i = 0; optlist[i].name; i++) { - if (optlist[i].val) - STPUTC(optlist[i].letter, expdest); - } - break; - case '@': - if (flag & EXP_FULL && quoted) { - for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { - STRTODEST(p); - if (*ap) - STPUTC('\0', expdest); - } - break; - } - /* fall through */ - case '*': - if (ifsset() != 0) - sep = ifsval()[0]; - else - sep = ' '; - for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { - STRTODEST(p); - if (*ap && sep) - STPUTC(sep, expdest); - } - break; - case '0': - p = arg0; - STRTODEST(p); - break; - default: - if (is_digit(*name)) { - num = atoi(name); - if (num > 0 && num <= shellparam.nparam) { - p = shellparam.p[num - 1]; - STRTODEST(p); - } - } - break; - } -} - - - -/* - * Record the fact that we have to scan this region of the - * string for IFS characters. - */ - -STATIC void -recordregion(int start, int end, int inquotes) -{ - struct ifsregion *ifsp; - - if (ifslastp == NULL) { - ifsp = &ifsfirst; - } else { - if (ifslastp->endoff == start - && ifslastp->inquotes == inquotes) { - /* extend previous area */ - ifslastp->endoff = end; - return; - } - ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); - ifslastp->next = ifsp; - } - ifslastp = ifsp; - ifslastp->next = NULL; - ifslastp->begoff = start; - ifslastp->endoff = end; - ifslastp->inquotes = inquotes; -} - - - -/* - * Break the argument string into pieces based upon IFS and add the - * strings to the argument list. The regions of the string to be - * searched for IFS characters have been stored by recordregion. - */ -STATIC void -ifsbreakup(char *string, struct arglist *arglist) -{ - struct ifsregion *ifsp; - struct strlist *sp; - char *start; - char *p; - char *q; - const char *ifs; - const char *ifsspc; - int inquotes; - - start = string; - ifsspc = NULL; - inquotes = 0; - - if (ifslastp == NULL) { - /* Return entire argument, IFS doesn't apply to any of it */ - sp = (struct strlist *)stalloc(sizeof *sp); - sp->text = start; - *arglist->lastp = sp; - arglist->lastp = &sp->next; - return; - } - - ifs = ifsset() ? ifsval() : " \t\n"; - - for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) { - p = string + ifsp->begoff; - inquotes = ifsp->inquotes; - ifsspc = NULL; - while (p < string + ifsp->endoff) { - q = p; - if (*p == CTLESC) - p++; - if (inquotes) { - /* Only NULs (probably from "$@") end args */ - if (*p != 0) { - p++; - continue; - } - } else { - if (!strchr(ifs, *p)) { - p++; - continue; - } - ifsspc = strchr(" \t\n", *p); - - /* Ignore IFS whitespace at start */ - if (q == start && ifsspc != NULL) { - p++; - start = p; - continue; - } - } - - /* Save this argument... */ - *q = '\0'; - sp = (struct strlist *)stalloc(sizeof *sp); - sp->text = start; - *arglist->lastp = sp; - arglist->lastp = &sp->next; - p++; - - if (ifsspc != NULL) { - /* Ignore further trailing IFS whitespace */ - for (; p < string + ifsp->endoff; p++) { - q = p; - if (*p == CTLESC) - p++; - if (strchr(ifs, *p) == NULL) { - p = q; - break; - } - if (strchr(" \t\n", *p) == NULL) { - p++; - break; - } - } - } - start = p; - } - } - - /* - * Save anything left as an argument. - * Traditionally we have treated 'IFS=':'; set -- x$IFS' as - * generating 2 arguments, the second of which is empty. - * Some recent clarification of the Posix spec say that it - * should only generate one.... - */ - if (*start /* || (!ifsspc && start > string) */) { - sp = (struct strlist *)stalloc(sizeof *sp); - sp->text = start; - *arglist->lastp = sp; - arglist->lastp = &sp->next; - } -} - -STATIC void -ifsfree(void) -{ - while (ifsfirst.next != NULL) { - struct ifsregion *ifsp; - INTOFF; - ifsp = ifsfirst.next->next; - ckfree(ifsfirst.next); - ifsfirst.next = ifsp; - INTON; - } - ifslastp = NULL; - ifsfirst.next = NULL; -} - - - -/* - * Expand shell metacharacters. At this point, the only control characters - * should be escapes. The results are stored in the list exparg. - */ - -char *expdir; - - -STATIC void -expandmeta(struct strlist *str, int flag) -{ - char *p; - struct strlist **savelastp; - struct strlist *sp; - char c; - /* TODO - EXP_REDIR */ - - while (str) { - if (fflag) - goto nometa; - p = str->text; - for (;;) { /* fast check for meta chars */ - if ((c = *p++) == '\0') - goto nometa; - if (c == '*' || c == '?' || c == '[' || c == '!') - break; - } - savelastp = exparg.lastp; - INTOFF; - if (expdir == NULL) { - int i = strlen(str->text); - expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ - } - - expmeta(expdir, str->text); - ckfree(expdir); - expdir = NULL; - INTON; - if (exparg.lastp == savelastp) { - /* - * no matches - */ -nometa: - *exparg.lastp = str; - rmescapes(str->text); - exparg.lastp = &str->next; - } else { - *exparg.lastp = NULL; - *savelastp = sp = expsort(*savelastp); - while (sp->next != NULL) - sp = sp->next; - exparg.lastp = &sp->next; - } - str = str->next; - } -} - - -/* - * Do metacharacter (i.e. *, ?, [...]) expansion. - */ - -STATIC void -expmeta(char *enddir, char *name) -{ - char *p; - const char *cp; - char *q; - char *start; - char *endname; - int metaflag; - struct stat statb; - DIR *dirp; - struct dirent *dp; - int atend; - int matchdot; - - metaflag = 0; - start = name; - for (p = name ; ; p++) { - if (*p == '*' || *p == '?') - metaflag = 1; - else if (*p == '[') { - q = p + 1; - if (*q == '!') - q++; - for (;;) { - while (*q == CTLQUOTEMARK) - q++; - if (*q == CTLESC) - q++; - if (*q == '/' || *q == '\0') - break; - if (*++q == ']') { - metaflag = 1; - break; - } - } - } else if (*p == '!' && p[1] == '!' && (p == name || p[-1] == '/')) { - metaflag = 1; - } else if (*p == '\0') - break; - else if (*p == CTLQUOTEMARK) - continue; - else if (*p == CTLESC) - p++; - if (*p == '/') { - if (metaflag) - break; - start = p + 1; - } - } - if (metaflag == 0) { /* we've reached the end of the file name */ - if (enddir != expdir) - metaflag++; - for (p = name ; ; p++) { - if (*p == CTLQUOTEMARK) - continue; - if (*p == CTLESC) - p++; - *enddir++ = *p; - if (*p == '\0') - break; - } - if (metaflag == 0 || lstat(expdir, &statb) >= 0) - addfname(expdir); - return; - } - endname = p; - if (start != name) { - p = name; - while (p < start) { - while (*p == CTLQUOTEMARK) - p++; - if (*p == CTLESC) - p++; - *enddir++ = *p++; - } - } - if (enddir == expdir) { - cp = "."; - } else if (enddir == expdir + 1 && *expdir == '/') { - cp = "/"; - } else { - cp = expdir; - enddir[-1] = '\0'; - } - if ((dirp = opendir(cp)) == NULL) - return; - if (enddir != expdir) - enddir[-1] = '/'; - if (*endname == 0) { - atend = 1; - } else { - atend = 0; - *endname++ = '\0'; - } - matchdot = 0; - p = start; - while (*p == CTLQUOTEMARK) - p++; - if (*p == CTLESC) - p++; - if (*p == '.') - matchdot++; - while (! int_pending() && (dp = readdir(dirp)) != NULL) { - if (dp->d_name[0] == '.' && ! matchdot) - continue; - if (patmatch(start, dp->d_name, 0)) { - if (atend) { - scopy(dp->d_name, enddir); - addfname(expdir); - } else { - for (p = enddir, cp = dp->d_name; - (*p++ = *cp++) != '\0';) - continue; - p[-1] = '/'; - expmeta(p, endname); - } - } - } - closedir(dirp); - if (! atend) - endname[-1] = '/'; -} - - -/* - * Add a file name to the list. - */ - -STATIC void -addfname(char *name) -{ - char *p; - struct strlist *sp; - - p = stalloc(strlen(name) + 1); - scopy(name, p); - sp = (struct strlist *)stalloc(sizeof *sp); - sp->text = p; - *exparg.lastp = sp; - exparg.lastp = &sp->next; -} - - -/* - * Sort the results of file name expansion. It calculates the number of - * strings to sort and then calls msort (short for merge sort) to do the - * work. - */ - -STATIC struct strlist * -expsort(struct strlist *str) -{ - int len; - struct strlist *sp; - - len = 0; - for (sp = str ; sp ; sp = sp->next) - len++; - return msort(str, len); -} - - -STATIC struct strlist * -msort(struct strlist *list, int len) -{ - struct strlist *p, *q = NULL; - struct strlist **lpp; - int half; - int n; - - if (len <= 1) - return list; - half = len >> 1; - p = list; - for (n = half ; --n >= 0 ; ) { - q = p; - p = p->next; - } - q->next = NULL; /* terminate first half of list */ - q = msort(list, half); /* sort first half of list */ - p = msort(p, len - half); /* sort second half */ - lpp = &list; - for (;;) { - if (strcmp(p->text, q->text) < 0) { - *lpp = p; - lpp = &p->next; - if ((p = *lpp) == NULL) { - *lpp = q; - break; - } - } else { - *lpp = q; - lpp = &q->next; - if ((q = *lpp) == NULL) { - *lpp = p; - break; - } - } - } - return list; -} - - - -/* - * Returns true if the pattern matches the string. - */ - -int -patmatch(char *pattern, char *string, int squoted) -{ -#ifdef notdef - if (pattern[0] == '!' && pattern[1] == '!') - return 1 - pmatch(pattern + 2, string); - else -#endif - return pmatch(pattern, string, squoted); -} - - -STATIC int -pmatch(char *pattern, char *string, int squoted) -{ - char *p, *q; - char c; - - p = pattern; - q = string; - for (;;) { - switch (c = *p++) { - case '\0': - goto breakloop; - case CTLESC: - if (squoted && *q == CTLESC) - q++; - if (*q++ != *p++) - return 0; - break; - case CTLQUOTEMARK: - continue; - case '?': - if (squoted && *q == CTLESC) - q++; - if (*q++ == '\0') - return 0; - break; - case '*': - c = *p; - while (c == CTLQUOTEMARK || c == '*') - c = *++p; - if (c != CTLESC && c != CTLQUOTEMARK && - c != '?' && c != '*' && c != '[') { - while (*q != c) { - if (squoted && *q == CTLESC && - q[1] == c) - break; - if (*q == '\0') - return 0; - if (squoted && *q == CTLESC) - q++; - q++; - } - } - do { - if (pmatch(p, q, squoted)) - return 1; - if (squoted && *q == CTLESC) - q++; - } while (*q++ != '\0'); - return 0; - case '[': { - char *endp; - int invert, found; - char chr; - - endp = p; - if (*endp == '!') - endp++; - for (;;) { - while (*endp == CTLQUOTEMARK) - endp++; - if (*endp == '\0') - goto dft; /* no matching ] */ - if (*endp == CTLESC) - endp++; - if (*++endp == ']') - break; - } - invert = 0; - if (*p == '!') { - invert++; - p++; - } - found = 0; - chr = *q++; - if (squoted && chr == CTLESC) - chr = *q++; - if (chr == '\0') - return 0; - c = *p++; - do { - if (c == CTLQUOTEMARK) - continue; - if (c == CTLESC) - c = *p++; - if (*p == '-' && p[1] != ']') { - p++; - while (*p == CTLQUOTEMARK) - p++; - if (*p == CTLESC) - p++; - if (chr >= c && chr <= *p) - found = 1; - p++; - } else { - if (chr == c) - found = 1; - } - } while ((c = *p++) != ']'); - if (found == invert) - return 0; - break; - } -dft: default: - if (squoted && *q == CTLESC) - q++; - if (*q++ != c) - return 0; - break; - } - } -breakloop: - if (*q != '\0') - return 0; - return 1; -} - - - -/* - * Remove any CTLESC characters from a string. - */ - -void -rmescapes(char *str) -{ - char *p, *q; - - p = str; - while (*p != CTLESC && *p != CTLQUOTEMARK) { - if (*p++ == '\0') - return; - } - q = p; - while (*p) { - if (*p == CTLQUOTEMARK) { - p++; - continue; - } - if (*p == CTLESC) - p++; - *q++ = *p++; - } - *q = '\0'; -} - - - -/* - * See if a pattern matches in a case statement. - */ - -int -casematch(union node *pattern, char *val) -{ - struct stackmark smark; - int result; - char *p; - - setstackmark(&smark); - argbackq = pattern->narg.backquote; - STARTSTACKSTR(expdest); - ifslastp = NULL; - argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); - STPUTC('\0', expdest); - p = grabstackstr(expdest); - result = patmatch(p, val, 0); - popstackmark(&smark); - return result; -} - -/* - * Our own itoa(). - */ - -STATIC char * -cvtnum(int num, char *buf) -{ - char temp[32]; - int neg = num < 0; - char *p = temp + 31; - - temp[31] = '\0'; - - do { - *--p = num % 10 + '0'; - } while ((num /= 10) != 0); - - if (neg) - *--p = '-'; - - while (*p) - STPUTC(*p++, buf); - return buf; -} - -/* - * Do most of the work for wordexp(3). - */ - -int -wordexpcmd(int argc, char **argv) -{ - size_t len; - int i; - - out1fmt("%d", argc - 1); - out1c('\0'); - for (i = 1, len = 0; i < argc; i++) - len += strlen(argv[i]); - out1fmt("%zd", len); - out1c('\0'); - for (i = 1; i < argc; i++) { - out1str(argv[i]); - out1c('\0'); - } - return (0); -} diff --git a/sh/expand.h b/sh/expand.h deleted file mode 100644 index 1ea876d..0000000 --- a/sh/expand.h +++ /dev/null @@ -1,72 +0,0 @@ -/* $NetBSD: expand.h,v 1.16 2004/07/13 15:05:59 seb 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 - * Kenneth Almquist. - * - * 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. - * - * @(#)expand.h 8.2 (Berkeley) 5/4/95 - */ - -struct strlist { - struct strlist *next; - char *text; -}; - - -struct arglist { - struct strlist *list; - struct strlist **lastp; -}; - -/* - * expandarg() flags - */ -#define EXP_FULL 0x1 /* perform word splitting & file globbing */ -#define EXP_TILDE 0x2 /* do normal tilde expansion */ -#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */ -#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */ -#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */ -#define EXP_IFS_SPLIT 0x20 /* need to record arguments for ifs breakup */ - - -union node; -void expandhere(union node *, int); -void expandarg(union node *, struct arglist *, int); -void expari(int); -int patmatch(char *, char *, int); -void rmescapes(char *); -int casematch(union node *, char *); -int wordexpcmd(int, char **); - -/* From arith.y */ -int arith(const char *); -int expcmd(int , char **); -void arith_lex_reset(void); -int yylex(void); diff --git a/sh/funcs/cmv b/sh/funcs/cmv deleted file mode 100644 index 667f846..0000000 --- a/sh/funcs/cmv +++ /dev/null @@ -1,50 +0,0 @@ -# $NetBSD: cmv,v 1.7 1995/05/11 21:31:05 christos 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 -# Kenneth Almquist. -# -# 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. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by the University of -# California, Berkeley and its contributors. -# 4. 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. -# -# @(#)cmv 8.2 (Berkeley) 5/4/95 - -# Conditional move--don't replace an existing file. - -cmv() { - if test $# != 2 - then echo "cmv: arg count" - return 2 - fi - if test -f "$2" -o -w "$2" - then echo "$2 exists" - return 2 - fi - /bin/mv "$1" "$2" -} diff --git a/sh/funcs/dirs b/sh/funcs/dirs deleted file mode 100644 index 68bb317..0000000 --- a/sh/funcs/dirs +++ /dev/null @@ -1,74 +0,0 @@ -# $NetBSD: dirs,v 1.7 1995/05/11 21:31:08 christos 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 -# Kenneth Almquist. -# -# 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. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by the University of -# California, Berkeley and its contributors. -# 4. 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. -# -# @(#)dirs 8.2 (Berkeley) 5/4/95 - -# pushd, popd, and dirs --- written by Chris Bertin -# Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris -# as modified by Patrick Elam of GTRI and Kenneth Almquist at UW - -pushd () { - SAVE=`pwd` - if [ "$1" = "" ] - then if [ "$DSTACK" = "" ] - then echo "pushd: directory stack empty." - return 1 - fi - set $DSTACK - cd $1 || return - shift 1 - DSTACK="$*" - else cd $1 > /dev/null || return - fi - DSTACK="$SAVE $DSTACK" - dirs -} - -popd () { - if [ "$DSTACK" = "" ] - then echo "popd: directory stack empty." - return 1 - fi - set $DSTACK - cd $1 - shift - DSTACK=$* - dirs -} - -dirs () { - echo "`pwd` $DSTACK" - return 0 -} diff --git a/sh/funcs/kill b/sh/funcs/kill deleted file mode 100644 index 75b0180..0000000 --- a/sh/funcs/kill +++ /dev/null @@ -1,50 +0,0 @@ -# $NetBSD: kill,v 1.7 1995/05/11 21:31:10 christos 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 -# Kenneth Almquist. -# -# 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. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by the University of -# California, Berkeley and its contributors. -# 4. 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. -# -# @(#)kill 8.2 (Berkeley) 5/4/95 - -# Convert job names to process ids and then run /bin/kill. - -kill() { - local args x - args= - for x in "$@" - do case $x in - %*) x=`jobid "$x"` ;; - esac - args="$args $x" - done - /bin/kill $args -} diff --git a/sh/funcs/login b/sh/funcs/login deleted file mode 100644 index 7ae08b2..0000000 --- a/sh/funcs/login +++ /dev/null @@ -1,39 +0,0 @@ -# $NetBSD: login,v 1.7 1995/05/11 21:31:11 christos 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 -# Kenneth Almquist. -# -# 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. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by the University of -# California, Berkeley and its contributors. -# 4. 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. -# -# @(#)login 8.2 (Berkeley) 5/4/95 - -# replaces the login builtin in the BSD shell -login () exec login "$@" diff --git a/sh/funcs/newgrp b/sh/funcs/newgrp deleted file mode 100644 index 796a4f1..0000000 --- a/sh/funcs/newgrp +++ /dev/null @@ -1,38 +0,0 @@ -# $NetBSD: newgrp,v 1.7 1995/05/11 21:31:12 christos 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 -# Kenneth Almquist. -# -# 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. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by the University of -# California, Berkeley and its contributors. -# 4. 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. -# -# @(#)newgrp 8.2 (Berkeley) 5/4/95 - -newgrp() exec newgrp "$@" diff --git a/sh/funcs/popd b/sh/funcs/popd deleted file mode 100644 index b2b65d5..0000000 --- a/sh/funcs/popd +++ /dev/null @@ -1,74 +0,0 @@ -# $NetBSD: popd,v 1.7 1995/05/11 21:31:13 christos 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 -# Kenneth Almquist. -# -# 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. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by the University of -# California, Berkeley and its contributors. -# 4. 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. -# -# @(#)popd 8.2 (Berkeley) 5/4/95 - -# pushd, popd, and dirs --- written by Chris Bertin -# Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris -# as modified by Patrick Elam of GTRI and Kenneth Almquist at UW - -pushd () { - SAVE=`pwd` - if [ "$1" = "" ] - then if [ "$DSTACK" = "" ] - then echo "pushd: directory stack empty." - return 1 - fi - set $DSTACK - cd $1 || return - shift 1 - DSTACK="$*" - else cd $1 > /dev/null || return - fi - DSTACK="$SAVE $DSTACK" - dirs -} - -popd () { - if [ "$DSTACK" = "" ] - then echo "popd: directory stack empty." - return 1 - fi - set $DSTACK - cd $1 - shift - DSTACK=$* - dirs -} - -dirs () { - echo "`pwd` $DSTACK" - return 0 -} diff --git a/sh/funcs/pushd b/sh/funcs/pushd deleted file mode 100644 index b393038..0000000 --- a/sh/funcs/pushd +++ /dev/null @@ -1,74 +0,0 @@ -# $NetBSD: pushd,v 1.7 1995/05/11 21:31:15 christos 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 -# Kenneth Almquist. -# -# 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. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by the University of -# California, Berkeley and its contributors. -# 4. 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. -# -# @(#)pushd 8.2 (Berkeley) 5/4/95 - -# pushd, popd, and dirs --- written by Chris Bertin -# Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris -# as modified by Patrick Elam of GTRI and Kenneth Almquist at UW - -pushd () { - SAVE=`pwd` - if [ "$1" = "" ] - then if [ "$DSTACK" = "" ] - then echo "pushd: directory stack empty." - return 1 - fi - set $DSTACK - cd $1 || return - shift 1 - DSTACK="$*" - else cd $1 > /dev/null || return - fi - DSTACK="$SAVE $DSTACK" - dirs -} - -popd () { - if [ "$DSTACK" = "" ] - then echo "popd: directory stack empty." - return 1 - fi - set $DSTACK - cd $1 - shift - DSTACK=$* - dirs -} - -dirs () { - echo "`pwd` $DSTACK" - return 0 -} diff --git a/sh/funcs/suspend b/sh/funcs/suspend deleted file mode 100644 index 8a4197d..0000000 --- a/sh/funcs/suspend +++ /dev/null @@ -1,42 +0,0 @@ -# $NetBSD: suspend,v 1.7 1995/05/11 21:31:17 christos 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 -# Kenneth Almquist. -# -# 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. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by the University of -# California, Berkeley and its contributors. -# 4. 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. -# -# @(#)suspend 8.2 (Berkeley) 5/4/95 - -suspend() { - local - - set +j - kill -TSTP 0 -} diff --git a/sh/histedit.c b/sh/histedit.c deleted file mode 100644 index 4bb2b34..0000000 --- a/sh/histedit.c +++ /dev/null @@ -1,540 +0,0 @@ -/* $NetBSD: histedit.c,v 1.34 2003/10/27 06:19:29 lukem Exp $ */ - -/*- - * Copyright (c) 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * 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[] = "@(#)histedit.c 8.2 (Berkeley) 5/4/95"; -#else -__RCSID("$NetBSD: histedit.c,v 1.34 2003/10/27 06:19:29 lukem Exp $"); -#endif -#endif /* not lint */ - -#include <sys/param.h> -#include <paths.h> -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -/* - * Editline and history functions (and glue). - */ -#include "shell.h" -#include "parser.h" -#include "var.h" -#include "options.h" -#include "main.h" -#include "output.h" -#include "mystring.h" -#include "myhistedit.h" -#include "error.h" -#ifndef SMALL -#include "eval.h" -#include "memalloc.h" - -#define MAXHISTLOOPS 4 /* max recursions through fc */ -#define DEFEDITOR "ed" /* default editor *should* be $EDITOR */ - -History *hist; /* history cookie */ -EditLine *el; /* editline cookie */ -int displayhist; -static FILE *el_in, *el_out; - -STATIC const char *fc_replace(const char *, char *, char *); - -#ifdef DEBUG -extern FILE *tracefile; -#endif - -/* - * Set history and editing status. Called whenever the status may - * have changed (figures out what to do). - */ -void -histedit(void) -{ - FILE *el_err; - -#define editing (Eflag || Vflag) - - if (iflag) { - if (!hist) { - /* - * turn history on - */ - INTOFF; - hist = history_init(); - INTON; - - if (hist != NULL) - sethistsize(histsizeval()); - else - out2str("sh: can't initialize history\n"); - } - if (editing && !el && isatty(0)) { /* && isatty(2) ??? */ - /* - * turn editing on - */ - char *term, *shname; - - INTOFF; - if (el_in == NULL) - el_in = fdopen(0, "r"); - if (el_out == NULL) - el_out = fdopen(2, "w"); - if (el_in == NULL || el_out == NULL) - goto bad; - el_err = el_out; -#if DEBUG - if (tracefile) - el_err = tracefile; -#endif - term = lookupvar("TERM"); - if (term) - setenv("TERM", term, 1); - else - unsetenv("TERM"); - shname = arg0; - if (shname[0] == '-') - shname++; - el = el_init(shname, el_in, el_out, el_err); - if (el != NULL) { - if (hist) - el_set(el, EL_HIST, history, hist); - el_set(el, EL_PROMPT, getprompt); - el_set(el, EL_SIGNAL, 1); - } else { -bad: - out2str("sh: can't initialize editing\n"); - } - INTON; - } else if (!editing && el) { - INTOFF; - el_end(el); - el = NULL; - INTON; - } - if (el) { - if (Vflag) - el_set(el, EL_EDITOR, "vi"); - else if (Eflag) - el_set(el, EL_EDITOR, "emacs"); - el_source(el, NULL); - } - } else { - INTOFF; - if (el) { /* no editing if not interactive */ - el_end(el); - el = NULL; - } - if (hist) { - history_end(hist); - hist = NULL; - } - INTON; - } -} - - -void -sethistsize(const char *hs) -{ - int histsize; - HistEvent he; - - if (hist != NULL) { - if (hs == NULL || *hs == '\0' || - (histsize = atoi(hs)) < 0) - histsize = 100; - history(hist, &he, H_SETSIZE, histsize); - } -} - -void -setterm(const char *term) -{ - if (el != NULL && term != NULL) - if (el_set(el, EL_TERMINAL, term) != 0) { - outfmt(out2, "sh: Can't set terminal type %s\n", term); - outfmt(out2, "sh: Using dumb terminal settings.\n"); - } -} - -int -inputrc(argc, argv) - int argc; - char **argv; -{ - if (argc != 2) { - out2str("usage: inputrc file\n"); - return 1; - } - if (el != NULL) { - if (el_source(el, argv[1])) { - out2str("inputrc: failed\n"); - return 1; - } else - return 0; - } else { - out2str("sh: inputrc ignored, not editing\n"); - return 1; - } -} - -/* - * This command is provided since POSIX decided to standardize - * the Korn shell fc command. Oh well... - */ -int -histcmd(int argc, char **argv) -{ - int ch; - const char *editor = NULL; - HistEvent he; - int lflg = 0, nflg = 0, rflg = 0, sflg = 0; - int i, retval; - const char *firststr, *laststr; - int first, last, direction; - char *pat = NULL, *repl; /* ksh "fc old=new" crap */ - static int active = 0; - struct jmploc jmploc; - struct jmploc *volatile savehandler; - char editfile[MAXPATHLEN + 1]; - FILE *efp; -#ifdef __GNUC__ - /* Avoid longjmp clobbering */ - (void) &editor; - (void) &lflg; - (void) &nflg; - (void) &rflg; - (void) &sflg; - (void) &firststr; - (void) &laststr; - (void) &pat; - (void) &repl; - (void) &efp; - (void) &argc; - (void) &argv; -#endif - - if (hist == NULL) - error("history not active"); - - if (argc == 1) - error("missing history argument"); - - optreset = 1; optind = 1; /* initialize getopt */ - while (not_fcnumber(argv[optind]) && - (ch = getopt(argc, argv, ":e:lnrs")) != -1) - switch ((char)ch) { - case 'e': - editor = optionarg; - break; - case 'l': - lflg = 1; - break; - case 'n': - nflg = 1; - break; - case 'r': - rflg = 1; - break; - case 's': - sflg = 1; - break; - case ':': - error("option -%c expects argument", optopt); - /* NOTREACHED */ - case '?': - default: - error("unknown option: -%c", optopt); - /* NOTREACHED */ - } - argc -= optind, argv += optind; - - /* - * If executing... - */ - if (lflg == 0 || editor || sflg) { - lflg = 0; /* ignore */ - editfile[0] = '\0'; - /* - * Catch interrupts to reset active counter and - * cleanup temp files. - */ - if (setjmp(jmploc.loc)) { - active = 0; - if (*editfile) - unlink(editfile); - handler = savehandler; - longjmp(handler->loc, 1); - } - savehandler = handler; - handler = &jmploc; - if (++active > MAXHISTLOOPS) { - active = 0; - displayhist = 0; - error("called recursively too many times"); - } - /* - * Set editor. - */ - if (sflg == 0) { - if (editor == NULL && - (editor = bltinlookup("FCEDIT", 1)) == NULL && - (editor = bltinlookup("EDITOR", 1)) == NULL) - editor = DEFEDITOR; - if (editor[0] == '-' && editor[1] == '\0') { - sflg = 1; /* no edit */ - editor = NULL; - } - } - } - - /* - * If executing, parse [old=new] now - */ - if (lflg == 0 && argc > 0 && - ((repl = strchr(argv[0], '=')) != NULL)) { - pat = argv[0]; - *repl++ = '\0'; - argc--, argv++; - } - /* - * determine [first] and [last] - */ - switch (argc) { - case 0: - firststr = lflg ? "-16" : "-1"; - laststr = "-1"; - break; - case 1: - firststr = argv[0]; - laststr = lflg ? "-1" : argv[0]; - break; - case 2: - firststr = argv[0]; - laststr = argv[1]; - break; - default: - error("too many args"); - /* NOTREACHED */ - } - /* - * Turn into event numbers. - */ - first = str_to_event(firststr, 0); - last = str_to_event(laststr, 1); - - if (rflg) { - i = last; - last = first; - first = i; - } - /* - * XXX - this should not depend on the event numbers - * always increasing. Add sequence numbers or offset - * to the history element in next (diskbased) release. - */ - direction = first < last ? H_PREV : H_NEXT; - - /* - * If editing, grab a temp file. - */ - if (editor) { - int fd; - INTOFF; /* easier */ - snprintf(editfile, sizeof(editfile), "%s_shXXXXXX", _PATH_TMP); - if ((fd = mkstemp(editfile)) < 0) - error("can't create temporary file %s", editfile); - if ((efp = fdopen(fd, "w")) == NULL) { - close(fd); - error("can't allocate stdio buffer for temp"); - } - } - - /* - * Loop through selected history events. If listing or executing, - * do it now. Otherwise, put into temp file and call the editor - * after. - * - * The history interface needs rethinking, as the following - * convolutions will demonstrate. - */ - history(hist, &he, H_FIRST); - retval = history(hist, &he, H_NEXT_EVENT, first); - for (;retval != -1; retval = history(hist, &he, direction)) { - if (lflg) { - if (!nflg) - out1fmt("%5d ", he.num); - out1str(he.str); - } else { - const char *s = pat ? - fc_replace(he.str, pat, repl) : he.str; - - if (sflg) { - if (displayhist) { - out2str(s); - } - - evalstring(strcpy(stalloc(strlen(s) + 1), s), 0); - if (displayhist && hist) { - /* - * XXX what about recursive and - * relative histnums. - */ - history(hist, &he, H_ENTER, s); - } - } else - fputs(s, efp); - } - /* - * At end? (if we were to lose last, we'd sure be - * messed up). - */ - if (he.num == last) - break; - } - if (editor) { - char *editcmd; - - fclose(efp); - editcmd = stalloc(strlen(editor) + strlen(editfile) + 2); - sprintf(editcmd, "%s %s", editor, editfile); - evalstring(editcmd, 0); /* XXX - should use no JC command */ - INTON; - readcmdfile(editfile); /* XXX - should read back - quick tst */ - unlink(editfile); - } - - if (lflg == 0 && active > 0) - --active; - if (displayhist) - displayhist = 0; - return 0; -} - -STATIC const char * -fc_replace(const char *s, char *p, char *r) -{ - char *dest; - int plen = strlen(p); - - STARTSTACKSTR(dest); - while (*s) { - if (*s == *p && strncmp(s, p, plen) == 0) { - while (*r) - STPUTC(*r++, dest); - s += plen; - *p = '\0'; /* so no more matches */ - } else - STPUTC(*s++, dest); - } - STACKSTRNUL(dest); - dest = grabstackstr(dest); - - return (dest); -} - -int -not_fcnumber(char *s) -{ - if (s == NULL) - return 0; - if (*s == '-') - s++; - return (!is_number(s)); -} - -int -str_to_event(const char *str, int last) -{ - HistEvent he; - const char *s = str; - int relative = 0; - int i, retval; - - retval = history(hist, &he, H_FIRST); - switch (*s) { - case '-': - relative = 1; - /*FALLTHROUGH*/ - case '+': - s++; - } - if (is_number(s)) { - i = atoi(s); - if (relative) { - while (retval != -1 && i--) { - retval = history(hist, &he, H_NEXT); - } - if (retval == -1) - retval = history(hist, &he, H_LAST); - } else { - retval = history(hist, &he, H_NEXT_EVENT, i); - if (retval == -1) { - /* - * the notion of first and last is - * backwards to that of the history package - */ - retval = history(hist, &he, - last ? H_FIRST : H_LAST); - } - } - if (retval == -1) - error("history number %s not found (internal error)", - str); - } else { - /* - * pattern - */ - retval = history(hist, &he, H_PREV_STR, str); - if (retval == -1) - error("history pattern not found: %s", str); - } - return (he.num); -} -#else -int -histcmd(int argc, char **argv) -{ - error("not compiled with history support"); - /* NOTREACHED */ -} -int -inputrc(int argc, char **argv) -{ - error("not compiled with history support"); - /* NOTREACHED */ -} -#endif diff --git a/sh/init.c b/sh/init.c deleted file mode 100644 index 55ad172..0000000 --- a/sh/init.c +++ /dev/null @@ -1,1090 +0,0 @@ -/* - * This file was generated by the mkinit program. - */ - -#include "shell.h" -#include "mystring.h" -#include "init.h" -#include "eval.h" -#include <stdio.h> -#include "input.h" -#include "error.h" -#include <stdlib.h> -#include "options.h" -#include "redir.h" -#include <signal.h> -#include "trap.h" -#include "output.h" -#include "memalloc.h" -#include "var.h" - - - -#undef ATABSIZE -#define ATABSIZE 39 -#undef YYBISON -#define YYBISON 1 -#undef YYSKELETON_NAME -#define YYSKELETON_NAME "yacc.c" -#undef YYPURE -#define YYPURE 0 -#undef YYLSP_NEEDED -#define YYLSP_NEEDED 0 -#undef ARITH_NUM -#define ARITH_NUM 258 -#undef ARITH_LPAREN -#define ARITH_LPAREN 259 -#undef ARITH_RPAREN -#define ARITH_RPAREN 260 -#undef ARITH_OR -#define ARITH_OR 261 -#undef ARITH_AND -#define ARITH_AND 262 -#undef ARITH_BOR -#define ARITH_BOR 263 -#undef ARITH_BXOR -#define ARITH_BXOR 264 -#undef ARITH_BAND -#define ARITH_BAND 265 -#undef ARITH_NE -#define ARITH_NE 266 -#undef ARITH_EQ -#define ARITH_EQ 267 -#undef ARITH_LE -#define ARITH_LE 268 -#undef ARITH_GE -#define ARITH_GE 269 -#undef ARITH_GT -#define ARITH_GT 270 -#undef ARITH_LT -#define ARITH_LT 271 -#undef ARITH_RSHIFT -#define ARITH_RSHIFT 272 -#undef ARITH_LSHIFT -#define ARITH_LSHIFT 273 -#undef ARITH_SUB -#define ARITH_SUB 274 -#undef ARITH_ADD -#define ARITH_ADD 275 -#undef ARITH_REM -#define ARITH_REM 276 -#undef ARITH_DIV -#define ARITH_DIV 277 -#undef ARITH_MUL -#define ARITH_MUL 278 -#undef ARITH_BNOT -#define ARITH_BNOT 279 -#undef ARITH_NOT -#define ARITH_NOT 280 -#undef ARITH_UNARYPLUS -#define ARITH_UNARYPLUS 281 -#undef ARITH_UNARYMINUS -#define ARITH_UNARYMINUS 282 -#undef YYFINAL -#define YYFINAL 14 -#undef YYLAST -#define YYLAST 170 -#undef YYNTOKENS -#define YYNTOKENS 28 -#undef YYNNTS -#define YYNNTS 3 -#undef YYNRULES -#define YYNRULES 26 -#undef YYNSTATES -#define YYNSTATES 52 -#undef YYUNDEFTOK -#define YYUNDEFTOK 2 -#undef YYMAXUTOK -#define YYMAXUTOK 282 -#undef YYPACT_NINF -#define YYPACT_NINF -13 -#undef YYTABLE_NINF -#define YYTABLE_NINF -1 -#undef yyerrok -#define yyerrok (yyerrstatus = 0) -#undef yyclearin -#define yyclearin (yychar = YYEMPTY) -#undef YYEMPTY -#define YYEMPTY (-2) -#undef YYEOF -#define YYEOF 0 -#undef YYACCEPT -#define YYACCEPT goto yyacceptlab -#undef YYABORT -#define YYABORT goto yyabortlab -#undef YYERROR -#define YYERROR goto yyerrorlab -#undef YYFAIL -#define YYFAIL goto yyerrlab -#undef YYTERROR -#define YYTERROR 1 -#undef YYERRCODE -#define YYERRCODE 256 -#undef YYPOPSTACK -#define YYPOPSTACK (yyvsp--, yyssp--) -#undef YY_INT_ALIGNED -#define YY_INT_ALIGNED short int -#undef FLEX_SCANNER -#define FLEX_SCANNER -#undef YY_FLEX_MAJOR_VERSION -#define YY_FLEX_MAJOR_VERSION 2 -#undef YY_FLEX_MINOR_VERSION -#define YY_FLEX_MINOR_VERSION 5 -#undef YY_FLEX_SUBMINOR_VERSION -#define YY_FLEX_SUBMINOR_VERSION 31 -#undef FLEX_BETA -#define FLEX_BETA -#undef FLEXINT_H -#define FLEXINT_H -#undef INT8_MIN -#define INT8_MIN (-128) -#undef INT16_MIN -#define INT16_MIN (-32767-1) -#undef INT32_MIN -#define INT32_MIN (-2147483647-1) -#undef INT8_MAX -#define INT8_MAX (127) -#undef INT16_MAX -#define INT16_MAX (32767) -#undef INT32_MAX -#define INT32_MAX (2147483647) -#undef UINT8_MAX -#define UINT8_MAX (255U) -#undef UINT16_MAX -#define UINT16_MAX (65535U) -#undef UINT32_MAX -#define UINT32_MAX (4294967295U) -#undef YY_USE_CONST -#define YY_USE_CONST -#undef YY_USE_CONST -#define YY_USE_CONST -#undef yyconst -#define yyconst const -#undef yyconst -#define yyconst -#undef YY_NULL -#define YY_NULL 0 -#undef BEGIN -#define BEGIN (yy_start) = 1 + 2 * -#undef YY_START -#define YY_START (((yy_start) - 1) / 2) -#undef YYSTATE -#define YYSTATE YY_START -#undef YY_NEW_FILE -#define YY_NEW_FILE yyrestart(yyin ) -#undef YY_END_OF_BUFFER_CHAR -#define YY_END_OF_BUFFER_CHAR 0 -#undef YY_BUF_SIZE -#define YY_BUF_SIZE 16384 -#undef YY_TYPEDEF_YY_BUFFER_STATE -#define YY_TYPEDEF_YY_BUFFER_STATE -#undef EOB_ACT_CONTINUE_SCAN -#define EOB_ACT_CONTINUE_SCAN 0 -#undef EOB_ACT_END_OF_FILE -#define EOB_ACT_END_OF_FILE 1 -#undef EOB_ACT_LAST_MATCH -#define EOB_ACT_LAST_MATCH 2 -#undef YY_TYPEDEF_YY_SIZE_T -#define YY_TYPEDEF_YY_SIZE_T -#undef YY_STRUCT_YY_BUFFER_STATE -#define YY_STRUCT_YY_BUFFER_STATE -#undef YY_BUFFER_NEW -#define YY_BUFFER_NEW 0 -#undef YY_BUFFER_NORMAL -#define YY_BUFFER_NORMAL 1 -#undef YY_BUFFER_EOF_PENDING -#define YY_BUFFER_EOF_PENDING 2 -#undef YY_CURRENT_BUFFER -#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ -#undef YY_CURRENT_BUFFER_LVALUE -#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] -#undef YY_FLUSH_BUFFER -#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER ) -#undef yy_new_buffer -#define yy_new_buffer yy_create_buffer -#undef YY_SKIP_YYWRAP -#define YY_SKIP_YYWRAP -#undef yytext_ptr -#define yytext_ptr yytext -#undef YY_DO_BEFORE_ACTION -#define YY_DO_BEFORE_ACTION \ -#undef YY_NUM_RULES -#define YY_NUM_RULES 29 -#undef YY_END_OF_BUFFER -#define YY_END_OF_BUFFER 30 -#undef REJECT -#define REJECT reject_used_but_not_detected -#undef YY_MORE_ADJ -#define YY_MORE_ADJ 0 -#undef YY_RESTORE_YY_MORE_OFFSET -#define YY_RESTORE_YY_MORE_OFFSET -#undef YY_NO_UNPUT -#define YY_NO_UNPUT -#undef INITIAL -#define INITIAL 0 -#undef YY_EXTRA_TYPE -#define YY_EXTRA_TYPE void * -#undef YY_READ_BUF_SIZE -#define YY_READ_BUF_SIZE 8192 -#undef ECHO -#define ECHO (void) fwrite( yytext, yyleng, 1, yyout ) -#undef YY_START_STACK_INCR -#define YY_START_STACK_INCR 25 -#undef YY_DECL_IS_OURS -#define YY_DECL_IS_OURS 1 -#undef YY_DECL -#define YY_DECL int yylex (void) -#undef YY_USER_ACTION -#define YY_USER_ACTION -#undef YY_BREAK -#define YY_BREAK break; -#undef YY_RULE_SETUP -#define YY_RULE_SETUP \ -#undef YY_EXIT_FAILURE -#define YY_EXIT_FAILURE 2 -#undef YYTABLES_NAME -#define YYTABLES_NAME "yytables" -#undef MAXPWD -#define MAXPWD 256 -#undef signal -#define signal bsd_signal -#undef ALL -#define ALL (E_OPEN|E_CREAT|E_EXEC) -#undef EV_EXIT -#define EV_EXIT 01 /* exit after evaluating tree */ -#undef EV_TESTED -#define EV_TESTED 02 /* exit status is checked; ignore -e flag */ -#undef EV_BACKCMD -#define EV_BACKCMD 04 /* command executing within back quotes */ -#undef CMDTABLESIZE -#define CMDTABLESIZE 31 /* should be prime */ -#undef ARB -#define ARB 1 /* actual size determined at run time */ -#undef NEWARGS -#define NEWARGS 5 -#undef EOF_NLEFT -#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */ -#undef _PATH_DEVNULL -#define _PATH_DEVNULL "/dev/null" -#undef PROFILE -#define PROFILE 0 -#undef SIGSSIZE -#define SIGSSIZE (sizeof(sigs)/sizeof(sigs[0])) -#undef MINSIZE -#define MINSIZE 504 /* minimum size of a block */ -#undef DEFINE_OPTIONS -#define DEFINE_OPTIONS -#undef EOFMARKLEN -#define EOFMARKLEN 79 -#undef OPENBRACE -#define OPENBRACE '{' -#undef CLOSEBRACE -#define CLOSEBRACE '}' -#undef EMPTY -#define EMPTY -2 /* marks an unused slot in redirtab */ -#undef signal -#define signal bsd_signal -#undef sys_signame -#define sys_signame sys_siglist -#undef S_DFL -#define S_DFL 1 /* default signal handling (SIG_DFL) */ -#undef S_CATCH -#define S_CATCH 2 /* signal is caught */ -#undef S_IGN -#define S_IGN 3 /* signal is ignored (SIG_IGN) */ -#undef S_HARD_IGN -#define S_HARD_IGN 4 /* signal is ignored permenantly */ -#undef S_RESET -#define S_RESET 5 /* temporary - to reset a hard ignored sig */ -#undef OUTBUFSIZ -#define OUTBUFSIZ BUFSIZ -#undef BLOCK_OUT -#define BLOCK_OUT -2 /* output to a fixed block of memory */ -#undef MEM_OUT -#define MEM_OUT -3 /* output to dynamically allocated memory */ -#undef OUTPUT_ERR -#define OUTPUT_ERR 01 /* error occurred on output */ -#undef TEMPSIZE -#define TEMPSIZE 24 -#undef HAVE_VASPRINTF -#define HAVE_VASPRINTF 1 -#undef VTABSIZE -#define VTABSIZE 39 -#undef VTABSIZE -#define VTABSIZE 517 -#undef ATABSIZE -#define ATABSIZE 39 -#undef YYBISON -#define YYBISON 1 -#undef YYSKELETON_NAME -#define YYSKELETON_NAME "yacc.c" -#undef YYPURE -#define YYPURE 0 -#undef YYLSP_NEEDED -#define YYLSP_NEEDED 0 -#undef ARITH_NUM -#define ARITH_NUM 258 -#undef ARITH_LPAREN -#define ARITH_LPAREN 259 -#undef ARITH_RPAREN -#define ARITH_RPAREN 260 -#undef ARITH_OR -#define ARITH_OR 261 -#undef ARITH_AND -#define ARITH_AND 262 -#undef ARITH_BOR -#define ARITH_BOR 263 -#undef ARITH_BXOR -#define ARITH_BXOR 264 -#undef ARITH_BAND -#define ARITH_BAND 265 -#undef ARITH_NE -#define ARITH_NE 266 -#undef ARITH_EQ -#define ARITH_EQ 267 -#undef ARITH_LE -#define ARITH_LE 268 -#undef ARITH_GE -#define ARITH_GE 269 -#undef ARITH_GT -#define ARITH_GT 270 -#undef ARITH_LT -#define ARITH_LT 271 -#undef ARITH_RSHIFT -#define ARITH_RSHIFT 272 -#undef ARITH_LSHIFT -#define ARITH_LSHIFT 273 -#undef ARITH_SUB -#define ARITH_SUB 274 -#undef ARITH_ADD -#define ARITH_ADD 275 -#undef ARITH_REM -#define ARITH_REM 276 -#undef ARITH_DIV -#define ARITH_DIV 277 -#undef ARITH_MUL -#define ARITH_MUL 278 -#undef ARITH_BNOT -#define ARITH_BNOT 279 -#undef ARITH_NOT -#define ARITH_NOT 280 -#undef ARITH_UNARYPLUS -#define ARITH_UNARYPLUS 281 -#undef ARITH_UNARYMINUS -#define ARITH_UNARYMINUS 282 -#undef YYFINAL -#define YYFINAL 14 -#undef YYLAST -#define YYLAST 170 -#undef YYNTOKENS -#define YYNTOKENS 28 -#undef YYNNTS -#define YYNNTS 3 -#undef YYNRULES -#define YYNRULES 26 -#undef YYNSTATES -#define YYNSTATES 52 -#undef YYUNDEFTOK -#define YYUNDEFTOK 2 -#undef YYMAXUTOK -#define YYMAXUTOK 282 -#undef YYPACT_NINF -#define YYPACT_NINF -13 -#undef YYTABLE_NINF -#define YYTABLE_NINF -1 -#undef yyerrok -#define yyerrok (yyerrstatus = 0) -#undef yyclearin -#define yyclearin (yychar = YYEMPTY) -#undef YYEMPTY -#define YYEMPTY (-2) -#undef YYEOF -#define YYEOF 0 -#undef YYACCEPT -#define YYACCEPT goto yyacceptlab -#undef YYABORT -#define YYABORT goto yyabortlab -#undef YYERROR -#define YYERROR goto yyerrorlab -#undef YYFAIL -#define YYFAIL goto yyerrlab -#undef YYTERROR -#define YYTERROR 1 -#undef YYERRCODE -#define YYERRCODE 256 -#undef YYPOPSTACK -#define YYPOPSTACK (yyvsp--, yyssp--) -#undef YY_INT_ALIGNED -#define YY_INT_ALIGNED short int -#undef FLEX_SCANNER -#define FLEX_SCANNER -#undef YY_FLEX_MAJOR_VERSION -#define YY_FLEX_MAJOR_VERSION 2 -#undef YY_FLEX_MINOR_VERSION -#define YY_FLEX_MINOR_VERSION 5 -#undef YY_FLEX_SUBMINOR_VERSION -#define YY_FLEX_SUBMINOR_VERSION 31 -#undef FLEX_BETA -#define FLEX_BETA -#undef FLEXINT_H -#define FLEXINT_H -#undef INT8_MIN -#define INT8_MIN (-128) -#undef INT16_MIN -#define INT16_MIN (-32767-1) -#undef INT32_MIN -#define INT32_MIN (-2147483647-1) -#undef INT8_MAX -#define INT8_MAX (127) -#undef INT16_MAX -#define INT16_MAX (32767) -#undef INT32_MAX -#define INT32_MAX (2147483647) -#undef UINT8_MAX -#define UINT8_MAX (255U) -#undef UINT16_MAX -#define UINT16_MAX (65535U) -#undef UINT32_MAX -#define UINT32_MAX (4294967295U) -#undef YY_USE_CONST -#define YY_USE_CONST -#undef YY_USE_CONST -#define YY_USE_CONST -#undef yyconst -#define yyconst const -#undef yyconst -#define yyconst -#undef YY_NULL -#define YY_NULL 0 -#undef BEGIN -#define BEGIN (yy_start) = 1 + 2 * -#undef YY_START -#define YY_START (((yy_start) - 1) / 2) -#undef YYSTATE -#define YYSTATE YY_START -#undef YY_NEW_FILE -#define YY_NEW_FILE yyrestart(yyin ) -#undef YY_END_OF_BUFFER_CHAR -#define YY_END_OF_BUFFER_CHAR 0 -#undef YY_BUF_SIZE -#define YY_BUF_SIZE 16384 -#undef YY_TYPEDEF_YY_BUFFER_STATE -#define YY_TYPEDEF_YY_BUFFER_STATE -#undef EOB_ACT_CONTINUE_SCAN -#define EOB_ACT_CONTINUE_SCAN 0 -#undef EOB_ACT_END_OF_FILE -#define EOB_ACT_END_OF_FILE 1 -#undef EOB_ACT_LAST_MATCH -#define EOB_ACT_LAST_MATCH 2 -#undef YY_TYPEDEF_YY_SIZE_T -#define YY_TYPEDEF_YY_SIZE_T -#undef YY_STRUCT_YY_BUFFER_STATE -#define YY_STRUCT_YY_BUFFER_STATE -#undef YY_BUFFER_NEW -#define YY_BUFFER_NEW 0 -#undef YY_BUFFER_NORMAL -#define YY_BUFFER_NORMAL 1 -#undef YY_BUFFER_EOF_PENDING -#define YY_BUFFER_EOF_PENDING 2 -#undef YY_CURRENT_BUFFER -#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ -#undef YY_CURRENT_BUFFER_LVALUE -#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] -#undef YY_FLUSH_BUFFER -#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER ) -#undef yy_new_buffer -#define yy_new_buffer yy_create_buffer -#undef YY_SKIP_YYWRAP -#define YY_SKIP_YYWRAP -#undef yytext_ptr -#define yytext_ptr yytext -#undef YY_DO_BEFORE_ACTION -#define YY_DO_BEFORE_ACTION \ -#undef YY_NUM_RULES -#define YY_NUM_RULES 29 -#undef YY_END_OF_BUFFER -#define YY_END_OF_BUFFER 30 -#undef REJECT -#define REJECT reject_used_but_not_detected -#undef YY_MORE_ADJ -#define YY_MORE_ADJ 0 -#undef YY_RESTORE_YY_MORE_OFFSET -#define YY_RESTORE_YY_MORE_OFFSET -#undef YY_NO_UNPUT -#define YY_NO_UNPUT -#undef INITIAL -#define INITIAL 0 -#undef YY_EXTRA_TYPE -#define YY_EXTRA_TYPE void * -#undef YY_READ_BUF_SIZE -#define YY_READ_BUF_SIZE 8192 -#undef ECHO -#define ECHO (void) fwrite( yytext, yyleng, 1, yyout ) -#undef YY_START_STACK_INCR -#define YY_START_STACK_INCR 25 -#undef YY_DECL_IS_OURS -#define YY_DECL_IS_OURS 1 -#undef YY_DECL -#define YY_DECL int yylex (void) -#undef YY_USER_ACTION -#define YY_USER_ACTION -#undef YY_BREAK -#define YY_BREAK break; -#undef YY_RULE_SETUP -#define YY_RULE_SETUP \ -#undef YY_EXIT_FAILURE -#define YY_EXIT_FAILURE 2 -#undef YYTABLES_NAME -#define YYTABLES_NAME "yytables" -#undef MAXPWD -#define MAXPWD 256 -#undef signal -#define signal bsd_signal -#undef ALL -#define ALL (E_OPEN|E_CREAT|E_EXEC) -#undef EV_EXIT -#define EV_EXIT 01 /* exit after evaluating tree */ -#undef EV_TESTED -#define EV_TESTED 02 /* exit status is checked; ignore -e flag */ -#undef EV_BACKCMD -#define EV_BACKCMD 04 /* command executing within back quotes */ -#undef CMDTABLESIZE -#define CMDTABLESIZE 31 /* should be prime */ -#undef ARB -#define ARB 1 /* actual size determined at run time */ -#undef NEWARGS -#define NEWARGS 5 -#undef EOF_NLEFT -#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */ -#undef _PATH_DEVNULL -#define _PATH_DEVNULL "/dev/null" -#undef PROFILE -#define PROFILE 0 -#undef SIGSSIZE -#define SIGSSIZE (sizeof(sigs)/sizeof(sigs[0])) -#undef MINSIZE -#define MINSIZE 504 /* minimum size of a block */ -#undef DEFINE_OPTIONS -#define DEFINE_OPTIONS -#undef EOFMARKLEN -#define EOFMARKLEN 79 -#undef OPENBRACE -#define OPENBRACE '{' -#undef CLOSEBRACE -#define CLOSEBRACE '}' -#undef EMPTY -#define EMPTY -2 /* marks an unused slot in redirtab */ -#undef signal -#define signal bsd_signal -#undef sys_signame -#define sys_signame sys_siglist -#undef S_DFL -#define S_DFL 1 /* default signal handling (SIG_DFL) */ -#undef S_CATCH -#define S_CATCH 2 /* signal is caught */ -#undef S_IGN -#define S_IGN 3 /* signal is ignored (SIG_IGN) */ -#undef S_HARD_IGN -#define S_HARD_IGN 4 /* signal is ignored permenantly */ -#undef S_RESET -#define S_RESET 5 /* temporary - to reset a hard ignored sig */ -#undef OUTBUFSIZ -#define OUTBUFSIZ BUFSIZ -#undef BLOCK_OUT -#define BLOCK_OUT -2 /* output to a fixed block of memory */ -#undef MEM_OUT -#define MEM_OUT -3 /* output to dynamically allocated memory */ -#undef OUTPUT_ERR -#define OUTPUT_ERR 01 /* error occurred on output */ -#undef TEMPSIZE -#define TEMPSIZE 24 -#undef HAVE_VASPRINTF -#define HAVE_VASPRINTF 1 -#undef VTABSIZE -#define VTABSIZE 39 -#undef VTABSIZE -#define VTABSIZE 517 -#undef main -#define main echocmd -#undef YYBISON -#define YYBISON 1 -#undef YYSKELETON_NAME -#define YYSKELETON_NAME "yacc.c" -#undef YYPURE -#define YYPURE 0 -#undef YYLSP_NEEDED -#define YYLSP_NEEDED 0 -#undef ARITH_NUM -#define ARITH_NUM 258 -#undef ARITH_LPAREN -#define ARITH_LPAREN 259 -#undef ARITH_RPAREN -#define ARITH_RPAREN 260 -#undef ARITH_OR -#define ARITH_OR 261 -#undef ARITH_AND -#define ARITH_AND 262 -#undef ARITH_BOR -#define ARITH_BOR 263 -#undef ARITH_BXOR -#define ARITH_BXOR 264 -#undef ARITH_BAND -#define ARITH_BAND 265 -#undef ARITH_NE -#define ARITH_NE 266 -#undef ARITH_EQ -#define ARITH_EQ 267 -#undef ARITH_LE -#define ARITH_LE 268 -#undef ARITH_GE -#define ARITH_GE 269 -#undef ARITH_GT -#define ARITH_GT 270 -#undef ARITH_LT -#define ARITH_LT 271 -#undef ARITH_RSHIFT -#define ARITH_RSHIFT 272 -#undef ARITH_LSHIFT -#define ARITH_LSHIFT 273 -#undef ARITH_SUB -#define ARITH_SUB 274 -#undef ARITH_ADD -#define ARITH_ADD 275 -#undef ARITH_REM -#define ARITH_REM 276 -#undef ARITH_DIV -#define ARITH_DIV 277 -#undef ARITH_MUL -#define ARITH_MUL 278 -#undef ARITH_BNOT -#define ARITH_BNOT 279 -#undef ARITH_NOT -#define ARITH_NOT 280 -#undef ARITH_UNARYPLUS -#define ARITH_UNARYPLUS 281 -#undef ARITH_UNARYMINUS -#define ARITH_UNARYMINUS 282 -#undef YYFINAL -#define YYFINAL 14 -#undef YYLAST -#define YYLAST 170 -#undef YYNTOKENS -#define YYNTOKENS 28 -#undef YYNNTS -#define YYNNTS 3 -#undef YYNRULES -#define YYNRULES 26 -#undef YYNSTATES -#define YYNSTATES 52 -#undef YYUNDEFTOK -#define YYUNDEFTOK 2 -#undef YYMAXUTOK -#define YYMAXUTOK 282 -#undef YYPACT_NINF -#define YYPACT_NINF -13 -#undef YYTABLE_NINF -#define YYTABLE_NINF -1 -#undef yyerrok -#define yyerrok (yyerrstatus = 0) -#undef yyclearin -#define yyclearin (yychar = YYEMPTY) -#undef YYEMPTY -#define YYEMPTY (-2) -#undef YYEOF -#define YYEOF 0 -#undef YYACCEPT -#define YYACCEPT goto yyacceptlab -#undef YYABORT -#define YYABORT goto yyabortlab -#undef YYERROR -#define YYERROR goto yyerrorlab -#undef YYFAIL -#define YYFAIL goto yyerrlab -#undef YYTERROR -#define YYTERROR 1 -#undef YYERRCODE -#define YYERRCODE 256 -#undef YYPOPSTACK -#define YYPOPSTACK (yyvsp--, yyssp--) -#undef YY_INT_ALIGNED -#define YY_INT_ALIGNED short int -#undef FLEX_SCANNER -#define FLEX_SCANNER -#undef YY_FLEX_MAJOR_VERSION -#define YY_FLEX_MAJOR_VERSION 2 -#undef YY_FLEX_MINOR_VERSION -#define YY_FLEX_MINOR_VERSION 5 -#undef YY_FLEX_SUBMINOR_VERSION -#define YY_FLEX_SUBMINOR_VERSION 31 -#undef FLEX_BETA -#define FLEX_BETA -#undef FLEXINT_H -#define FLEXINT_H -#undef INT8_MIN -#define INT8_MIN (-128) -#undef INT16_MIN -#define INT16_MIN (-32767-1) -#undef INT32_MIN -#define INT32_MIN (-2147483647-1) -#undef INT8_MAX -#define INT8_MAX (127) -#undef INT16_MAX -#define INT16_MAX (32767) -#undef INT32_MAX -#define INT32_MAX (2147483647) -#undef UINT8_MAX -#define UINT8_MAX (255U) -#undef UINT16_MAX -#define UINT16_MAX (65535U) -#undef UINT32_MAX -#define UINT32_MAX (4294967295U) -#undef YY_USE_CONST -#define YY_USE_CONST -#undef YY_USE_CONST -#define YY_USE_CONST -#undef yyconst -#define yyconst const -#undef yyconst -#define yyconst -#undef YY_NULL -#define YY_NULL 0 -#undef BEGIN -#define BEGIN (yy_start) = 1 + 2 * -#undef YY_START -#define YY_START (((yy_start) - 1) / 2) -#undef YYSTATE -#define YYSTATE YY_START -#undef YY_NEW_FILE -#define YY_NEW_FILE yyrestart(yyin ) -#undef YY_END_OF_BUFFER_CHAR -#define YY_END_OF_BUFFER_CHAR 0 -#undef YY_BUF_SIZE -#define YY_BUF_SIZE 16384 -#undef YY_TYPEDEF_YY_BUFFER_STATE -#define YY_TYPEDEF_YY_BUFFER_STATE -#undef EOB_ACT_CONTINUE_SCAN -#define EOB_ACT_CONTINUE_SCAN 0 -#undef EOB_ACT_END_OF_FILE -#define EOB_ACT_END_OF_FILE 1 -#undef EOB_ACT_LAST_MATCH -#define EOB_ACT_LAST_MATCH 2 -#undef YY_TYPEDEF_YY_SIZE_T -#define YY_TYPEDEF_YY_SIZE_T -#undef YY_STRUCT_YY_BUFFER_STATE -#define YY_STRUCT_YY_BUFFER_STATE -#undef YY_BUFFER_NEW -#define YY_BUFFER_NEW 0 -#undef YY_BUFFER_NORMAL -#define YY_BUFFER_NORMAL 1 -#undef YY_BUFFER_EOF_PENDING -#define YY_BUFFER_EOF_PENDING 2 -#undef YY_CURRENT_BUFFER -#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ -#undef YY_CURRENT_BUFFER_LVALUE -#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] -#undef YY_FLUSH_BUFFER -#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER ) -#undef yy_new_buffer -#define yy_new_buffer yy_create_buffer -#undef yytext_ptr -#define yytext_ptr yytext -#undef YY_DO_BEFORE_ACTION -#define YY_DO_BEFORE_ACTION \ -#undef YY_NUM_RULES -#define YY_NUM_RULES 29 -#undef YY_END_OF_BUFFER -#define YY_END_OF_BUFFER 30 -#undef REJECT -#define REJECT reject_used_but_not_detected -#undef YY_MORE_ADJ -#define YY_MORE_ADJ 0 -#undef YY_RESTORE_YY_MORE_OFFSET -#define YY_RESTORE_YY_MORE_OFFSET -#undef YY_NO_UNPUT -#define YY_NO_UNPUT -#undef INITIAL -#define INITIAL 0 -#undef YY_EXTRA_TYPE -#define YY_EXTRA_TYPE void * -#undef YY_READ_BUF_SIZE -#define YY_READ_BUF_SIZE 8192 -#undef ECHO -#define ECHO (void) fwrite( yytext, yyleng, 1, yyout ) -#undef YY_START_STACK_INCR -#define YY_START_STACK_INCR 25 -#undef YY_DECL_IS_OURS -#define YY_DECL_IS_OURS 1 -#undef YY_DECL -#define YY_DECL int yylex (void) -#undef YY_USER_ACTION -#define YY_USER_ACTION -#undef YY_BREAK -#define YY_BREAK break; -#undef YY_RULE_SETUP -#define YY_RULE_SETUP \ -#undef YY_EXIT_FAILURE -#define YY_EXIT_FAILURE 2 -#undef YYTABLES_NAME -#define YYTABLES_NAME "yytables" -#undef MAXPWD -#define MAXPWD 256 -#undef ALL -#define ALL (E_OPEN|E_CREAT|E_EXEC) -#undef EV_EXIT -#define EV_EXIT 01 /* exit after evaluating tree */ -#undef EV_TESTED -#define EV_TESTED 02 /* exit status is checked; ignore -e flag */ -#undef EV_BACKCMD -#define EV_BACKCMD 04 /* command executing within back quotes */ -#undef CMDTABLESIZE -#define CMDTABLESIZE 31 /* should be prime */ -#undef ARB -#define ARB 1 /* actual size determined at run time */ -#undef NEWARGS -#define NEWARGS 5 -#undef EOF_NLEFT -#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */ -#undef _PATH_DEVNULL -#define _PATH_DEVNULL "/dev/null" -#undef PROFILE -#define PROFILE 0 -#undef SIGSSIZE -#define SIGSSIZE (sizeof(sigs)/sizeof(sigs[0])) -#undef MINSIZE -#define MINSIZE 504 /* minimum size of a block */ -#undef DEFINE_OPTIONS -#define DEFINE_OPTIONS -#undef EOFMARKLEN -#define EOFMARKLEN 79 -#undef OPENBRACE -#define OPENBRACE '{' -#undef CLOSEBRACE -#define CLOSEBRACE '}' -#undef EMPTY -#define EMPTY -2 /* marks an unused slot in redirtab */ -#undef S_DFL -#define S_DFL 1 /* default signal handling (SIG_DFL) */ -#undef S_CATCH -#define S_CATCH 2 /* signal is caught */ -#undef S_IGN -#define S_IGN 3 /* signal is ignored (SIG_IGN) */ -#undef S_HARD_IGN -#define S_HARD_IGN 4 /* signal is ignored permenantly */ -#undef S_RESET -#define S_RESET 5 /* temporary - to reset a hard ignored sig */ -#undef OUTBUFSIZ -#define OUTBUFSIZ BUFSIZ -#undef BLOCK_OUT -#define BLOCK_OUT -2 /* output to a fixed block of memory */ -#undef MEM_OUT -#define MEM_OUT -3 /* output to dynamically allocated memory */ -#undef OUTPUT_ERR -#define OUTPUT_ERR 01 /* error occurred on output */ -#undef TEMPSIZE -#define TEMPSIZE 24 -#undef HAVE_VASPRINTF -#define HAVE_VASPRINTF 1 -#undef VTABSIZE -#define VTABSIZE 39 -#undef VTABSIZE -#define VTABSIZE 517 -#undef main -#define main echocmd - - - -extern void rmaliases(void); - -extern int loopnest; /* current loop nesting level */ - -extern void deletefuncs(void); -extern void hash_special_builtins(void); - -struct strpush { - struct strpush *prev; /* preceding string on stack */ - char *prevstring; - int prevnleft; - int prevlleft; - struct alias *ap; /* if push was associated with an alias */ -}; - -struct parsefile { - struct parsefile *prev; /* preceding file on stack */ - int linno; /* current line */ - int fd; /* file descriptor (or -1 if string) */ - int nleft; /* number of chars left in this line */ - int lleft; /* number of chars left in this buffer */ - char *nextc; /* next char in buffer */ - char *buf; /* input buffer */ - struct strpush *strpush; /* for pushing strings at this level */ - struct strpush basestrpush; /* so pushing one is fast */ -}; - -extern int parselleft; /* copy of parsefile->lleft */ -extern struct parsefile basepf; /* top level input file */ -extern char basebuf[BUFSIZ]; /* buffer for top level input file */ - -extern pid_t backgndpid; /* pid of last background process */ -extern int jobctl; - -extern int tokpushback; /* last token pushed back */ -extern int checkkwd; /* 1 == check for kwds, 2 == also eat newlines */ - -struct redirtab { - struct redirtab *next; - short renamed[10]; -}; - -extern struct redirtab *redirlist; - -extern char sigmode[NSIG]; /* current value of signal */ - -extern char **environ; - - - -/* - * Initialization code. - */ - -void -init() { - - /* from exec.c: */ - { - hash_special_builtins(); - } - - /* from input.c: */ - { - basepf.nextc = basepf.buf = basebuf; - } - - /* from var.c: */ - { - char **envp; - - initvar(); - for (envp = environ ; *envp ; envp++) { - if (strchr(*envp, '=')) { - setvareq(*envp, VEXPORT|VTEXTFIXED); - } - } - } -} - - - -/* - * This routine is called when an error or an interrupt occurs in an - * interactive shell and control is returned to the main command loop. - */ - -void -reset() { - - /* from eval.c: */ - { - evalskip = 0; - loopnest = 0; - funcnest = 0; - } - - /* from input.c: */ - { - if (exception != EXSHELLPROC) - parselleft = parsenleft = 0; /* clear input buffer */ - popallfiles(); - } - - /* from parser.c: */ - { - tokpushback = 0; - checkkwd = 0; - } - - /* from redir.c: */ - { - while (redirlist) - popredir(); - } - - /* from output.c: */ - { - out1 = &output; - out2 = &errout; - if (memout.buf != NULL) { - ckfree(memout.buf); - memout.buf = NULL; - } - } -} - - - -/* - * This routine is called to initialize the shell to run a shell procedure. - */ - -void -initshellproc() { - - /* from alias.c: */ - { - rmaliases(); - } - - /* from eval.c: */ - { - exitstatus = 0; - } - - /* from exec.c: */ - { - deletefuncs(); - } - - /* from input.c: */ - { - popallfiles(); - } - - /* from jobs.c: */ - { - backgndpid = -1; -#if JOBS - jobctl = 0; -#endif - } - - /* from options.c: */ - { - int i; - - for (i = 0; optlist[i].name; i++) - optlist[i].val = 0; - optschanged(); - - } - - /* from redir.c: */ - { - clearredir(0); - } - - /* from trap.c: */ - { - char *sm; - - clear_traps(0); - for (sm = sigmode ; sm < sigmode + NSIG ; sm++) { - if (*sm == S_IGN) - *sm = S_HARD_IGN; - } - } - - /* from var.c: */ - { - shprocvar(); - } -} diff --git a/sh/init.h b/sh/init.h deleted file mode 100644 index 60d924e..0000000 --- a/sh/init.h +++ /dev/null @@ -1,39 +0,0 @@ -/* $NetBSD: init.h,v 1.10 2003/08/07 09:05:32 agc 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 - * Kenneth Almquist. - * - * 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. - * - * @(#)init.h 8.2 (Berkeley) 5/4/95 - */ - -void init(void); -void reset(void); -void initshellproc(void); diff --git a/sh/input.c b/sh/input.c deleted file mode 100644 index 056ee8b..0000000 --- a/sh/input.c +++ /dev/null @@ -1,577 +0,0 @@ -/* $NetBSD: input.c,v 1.39 2003/08/07 09:05:32 agc 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 - * Kenneth Almquist. - * - * 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[] = "@(#)input.c 8.3 (Berkeley) 6/9/95"; -#else -__RCSID("$NetBSD: input.c,v 1.39 2003/08/07 09:05:32 agc Exp $"); -#endif -#endif /* not lint */ - -#include <stdio.h> /* defines BUFSIZ */ -#include <fcntl.h> -#include <errno.h> -#include <unistd.h> -#include <stdlib.h> -#include <string.h> - -/* - * This file implements the input routines used by the parser. - */ - -#include "shell.h" -#include "redir.h" -#include "syntax.h" -#include "input.h" -#include "output.h" -#include "options.h" -#include "memalloc.h" -#include "error.h" -#include "alias.h" -#include "parser.h" -#include "myhistedit.h" - -#ifdef WITH_LINENOISE -#include "linenoise.h" -#endif - -#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */ - -MKINIT -struct strpush { - struct strpush *prev; /* preceding string on stack */ - char *prevstring; - int prevnleft; - int prevlleft; - struct alias *ap; /* if push was associated with an alias */ -}; - -/* - * The parsefile structure pointed to by the global variable parsefile - * contains information about the current file being read. - */ - -MKINIT -struct parsefile { - struct parsefile *prev; /* preceding file on stack */ - int linno; /* current line */ - int fd; /* file descriptor (or -1 if string) */ - int nleft; /* number of chars left in this line */ - int lleft; /* number of chars left in this buffer */ - char *nextc; /* next char in buffer */ - char *buf; /* input buffer */ - struct strpush *strpush; /* for pushing strings at this level */ - struct strpush basestrpush; /* so pushing one is fast */ -}; - - -int plinno = 1; /* input line number */ -int parsenleft; /* copy of parsefile->nleft */ -MKINIT int parselleft; /* copy of parsefile->lleft */ -char *parsenextc; /* copy of parsefile->nextc */ -MKINIT struct parsefile basepf; /* top level input file */ -MKINIT char basebuf[BUFSIZ]; /* buffer for top level input file */ -struct parsefile *parsefile = &basepf; /* current input file */ -int init_editline = 0; /* editline library initialized? */ -int whichprompt; /* 1 == PS1, 2 == PS2 */ - -#if WITH_HISTORY -EditLine *el; /* cookie for editline package */ -#endif - -STATIC void pushfile(void); -static int preadfd(void); - -#ifdef mkinit -INCLUDE <stdio.h> -INCLUDE "input.h" -INCLUDE "error.h" - -INIT { - basepf.nextc = basepf.buf = basebuf; -} - -RESET { - if (exception != EXSHELLPROC) - parselleft = parsenleft = 0; /* clear input buffer */ - popallfiles(); -} - -SHELLPROC { - popallfiles(); -} -#endif - - -/* - * Read a line from the script. - */ - -char * -pfgets(char *line, int len) -{ - char *p = line; - int nleft = len; - int c; - - while (--nleft > 0) { - c = pgetc_macro(); - if (c == PEOF) { - if (p == line) - return NULL; - break; - } - *p++ = c; - if (c == '\n') - break; - } - *p = '\0'; - return line; -} - - - -/* - * Read a character from the script, returning PEOF on end of file. - * Nul characters in the input are silently discarded. - */ - -int -pgetc(void) -{ - return pgetc_macro(); -} - -int in_interactive_mode() { - return parsefile != NULL && parsefile->fd == 0; -} - -static int -preadfd(void) -{ - int nr; - char *buf = parsefile->buf; - parsenextc = buf; - -retry: -#ifdef WITH_HISTORY - if (parsefile->fd == 0 && el) { - static const char *rl_cp; - static int el_len; - - if (rl_cp == NULL) - rl_cp = el_gets(el, &el_len); - if (rl_cp == NULL) - nr = 0; - else { - nr = el_len; - if (nr > BUFSIZ - 8) - nr = BUFSIZ - 8; - memcpy(buf, rl_cp, nr); - if (nr != el_len) { - el_len -= nr; - rl_cp += nr; - } else - rl_cp = 0; - } - - } else -#endif -#ifdef WITH_LINENOISE - if (parsefile->fd == 0) { - static char *rl_start; - static const char *rl_cp; - static int el_len; - - if (rl_cp == NULL) { - rl_cp = rl_start = linenoise(getprompt("")); - if (rl_cp != NULL) { - el_len = strlen(rl_start); - if (el_len != 0) { - /* Add non-blank lines to history. */ - linenoiseHistoryAdd(rl_start); - } - out2str("\n"); - /* Client expects a newline at end of input, doesn't expect null */ - rl_start[el_len++] = '\n'; - } - } - if (rl_cp == NULL) - nr = 0; - else { - nr = el_len; - if (nr > BUFSIZ - 8) - nr = BUFSIZ - 8; - memcpy(buf, rl_cp, nr); - if (nr != el_len) { - el_len -= nr; - rl_cp += nr; - } else { - rl_cp = 0; - if (rl_start != NULL) { - free(rl_start); - rl_start = NULL; - } - } - } - } else -#endif - nr = read(parsefile->fd, buf, BUFSIZ - 8); - - - if (nr <= 0) { - if (nr < 0) { - if (errno == EINTR) - goto retry; - if (parsefile->fd == 0 && errno == EWOULDBLOCK) { - int flags = fcntl(0, F_GETFL, 0); - if (flags >= 0 && flags & O_NONBLOCK) { - flags &=~ O_NONBLOCK; - if (fcntl(0, F_SETFL, flags) >= 0) { - out2str("sh: turning off NDELAY mode\n"); - goto retry; - } - } - } - } - nr = -1; - } - return nr; -} - -/* - * Refill the input buffer and return the next input character: - * - * 1) If a string was pushed back on the input, pop it; - * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading - * from a string so we can't refill the buffer, return EOF. - * 3) If the is more stuff in this buffer, use it else call read to fill it. - * 4) Process input up to the next newline, deleting nul characters. - */ - -int -preadbuffer(void) -{ - char *p, *q; - int more; - int something; - char savec; - - if (parsefile->strpush) { - popstring(); - if (--parsenleft >= 0) - return (*parsenextc++); - } - if (parsenleft == EOF_NLEFT || parsefile->buf == NULL) - return PEOF; - flushout(&output); - flushout(&errout); - -again: - if (parselleft <= 0) { - if ((parselleft = preadfd()) == -1) { - parselleft = parsenleft = EOF_NLEFT; - return PEOF; - } - } - - q = p = parsenextc; - - /* delete nul characters */ - something = 0; - for (more = 1; more;) { - switch (*p) { - case '\0': - p++; /* Skip nul */ - goto check; - - case '\t': - case ' ': - break; - - case '\n': - parsenleft = q - parsenextc; - more = 0; /* Stop processing here */ - break; - - default: - something = 1; - break; - } - - *q++ = *p++; -check: - if (--parselleft <= 0) { - parsenleft = q - parsenextc - 1; - if (parsenleft < 0) - goto again; - *q = '\0'; - more = 0; - } - } - - savec = *q; - *q = '\0'; - -#ifdef WITH_HISTORY - if (parsefile->fd == 0 && hist && something) { - HistEvent he; - INTOFF; - history(hist, &he, whichprompt == 1? H_ENTER : H_APPEND, - parsenextc); - INTON; - } -#endif - - if (vflag) { - out2str(parsenextc); - flushout(out2); - } - - *q = savec; - - return *parsenextc++; -} - -/* - * Undo the last call to pgetc. Only one character may be pushed back. - * PEOF may be pushed back. - */ - -void -pungetc(void) -{ - parsenleft++; - parsenextc--; -} - -/* - * Push a string back onto the input at this current parsefile level. - * We handle aliases this way. - */ -void -pushstring(char *s, int len, void *ap) -{ - struct strpush *sp; - - INTOFF; -/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/ - if (parsefile->strpush) { - sp = ckmalloc(sizeof (struct strpush)); - sp->prev = parsefile->strpush; - parsefile->strpush = sp; - } else - sp = parsefile->strpush = &(parsefile->basestrpush); - sp->prevstring = parsenextc; - sp->prevnleft = parsenleft; - sp->prevlleft = parselleft; - sp->ap = (struct alias *)ap; - if (ap) - ((struct alias *)ap)->flag |= ALIASINUSE; - parsenextc = s; - parsenleft = len; - INTON; -} - -void -popstring(void) -{ - struct strpush *sp = parsefile->strpush; - - INTOFF; - parsenextc = sp->prevstring; - parsenleft = sp->prevnleft; - parselleft = sp->prevlleft; -/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/ - if (sp->ap) - sp->ap->flag &= ~ALIASINUSE; - parsefile->strpush = sp->prev; - if (sp != &(parsefile->basestrpush)) - ckfree(sp); - INTON; -} - -/* - * Set the input to take input from a file. If push is set, push the - * old input onto the stack first. - */ - -void -setinputfile(const char *fname, int push) -{ - int fd; - int fd2; - - INTOFF; - if ((fd = open(fname, O_RDONLY)) < 0) - error("Can't open %s", fname); - if (fd < 10) { - fd2 = copyfd(fd, 10); - close(fd); - if (fd2 < 0) - error("Out of file descriptors"); - fd = fd2; - } - setinputfd(fd, push); - INTON; -} - - -/* - * Like setinputfile, but takes an open file descriptor. Call this with - * interrupts off. - */ - -void -setinputfd(int fd, int push) -{ - (void) fcntl(fd, F_SETFD, FD_CLOEXEC); - if (push) { - pushfile(); - parsefile->buf = ckmalloc(BUFSIZ); - } - if (parsefile->fd > 0) - close(parsefile->fd); - parsefile->fd = fd; - if (parsefile->buf == NULL) - parsefile->buf = ckmalloc(BUFSIZ); - parselleft = parsenleft = 0; - plinno = 1; -} - - -/* - * Like setinputfile, but takes input from a string. - */ - -void -setinputstring(char *string, int push) -{ - INTOFF; - if (push) - pushfile(); - parsenextc = string; - parselleft = parsenleft = strlen(string); - parsefile->buf = NULL; - plinno = 1; - INTON; -} - - - -/* - * To handle the "." command, a stack of input files is used. Pushfile - * adds a new entry to the stack and popfile restores the previous level. - */ - -STATIC void -pushfile(void) -{ - struct parsefile *pf; - - parsefile->nleft = parsenleft; - parsefile->lleft = parselleft; - parsefile->nextc = parsenextc; - parsefile->linno = plinno; - pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile)); - pf->prev = parsefile; - pf->fd = -1; - pf->strpush = NULL; - pf->basestrpush.prev = NULL; - parsefile = pf; -} - - -void -popfile(void) -{ - struct parsefile *pf = parsefile; - - INTOFF; - if (pf->fd >= 0) - close(pf->fd); - if (pf->buf) - ckfree(pf->buf); - while (pf->strpush) - popstring(); - parsefile = pf->prev; - ckfree(pf); - parsenleft = parsefile->nleft; - parselleft = parsefile->lleft; - parsenextc = parsefile->nextc; - plinno = parsefile->linno; - INTON; -} - - -/* - * Return to top level. - */ - -void -popallfiles(void) -{ - while (parsefile != &basepf) - popfile(); -} - - - -/* - * Close the file(s) that the shell is reading commands from. Called - * after a fork is done. - * - * Takes one arg, vfork, which tells it to not modify its global vars - * as it is still running in the parent. - * - * This code is (probably) unnecessary as the 'close on exec' flag is - * set and should be enough. In the vfork case it is definitely wrong - * to close the fds as another fork() may be done later to feed data - * from a 'here' document into a pipe and we don't want to close the - * pipe! - */ - -void -closescript(int vforked) -{ - if (vforked) - return; - popallfiles(); - if (parsefile->fd > 0) { - close(parsefile->fd); - parsefile->fd = 0; - } -} diff --git a/sh/input.h b/sh/input.h deleted file mode 100644 index 99c1b77..0000000 --- a/sh/input.h +++ /dev/null @@ -1,63 +0,0 @@ -/* $NetBSD: input.h,v 1.15 2003/08/07 09:05:33 agc 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 - * Kenneth Almquist. - * - * 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. - * - * @(#)input.h 8.2 (Berkeley) 5/4/95 - */ - -/* PEOF (the end of file marker) is defined in syntax.h */ - -/* - * The input line number. Input.c just defines this variable, and saves - * and restores it when files are pushed and popped. The user of this - * package must set its value. - */ -extern int plinno; -extern int parsenleft; /* number of characters left in input buffer */ -extern char *parsenextc; /* next character in input buffer */ -extern int init_editline; /* 0 == not setup, 1 == OK, -1 == failed */ - -int in_interactive_mode(); -char *pfgets(char *, int); -int pgetc(void); -int preadbuffer(void); -void pungetc(void); -void pushstring(char *, int, void *); -void popstring(void); -void setinputfile(const char *, int); -void setinputfd(int, int); -void setinputstring(char *, int); -void popfile(void); -void popallfiles(void); -void closescript(int); - -#define pgetc_macro() (--parsenleft >= 0? *parsenextc++ : preadbuffer()) diff --git a/sh/jobs.c b/sh/jobs.c deleted file mode 100644 index b9460b0..0000000 --- a/sh/jobs.c +++ /dev/null @@ -1,1487 +0,0 @@ -/* $NetBSD: jobs.c,v 1.62 2003/12/18 00:56:05 christos 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 - * Kenneth Almquist. - * - * 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[] = "@(#)jobs.c 8.5 (Berkeley) 5/4/95"; -#else -__RCSID("$NetBSD: jobs.c,v 1.62 2003/12/18 00:56:05 christos Exp $"); -#endif -#endif /* not lint */ - -#include <fcntl.h> -#include <signal.h> -#include <errno.h> -#include <unistd.h> -#include <stdlib.h> -#define _PATH_DEVNULL "/dev/null" -#include <sys/types.h> -#include <sys/param.h> -#ifdef BSD -#include <sys/wait.h> -#include <sys/time.h> -#include <sys/resource.h> -#endif -#include <sys/wait.h> -#define killpg(s,i) kill(-(s),i) -#include <sys/ioctl.h> - -#include "shell.h" -#if JOBS -#if OLD_TTY_DRIVER -#include "sgtty.h" -#else -#include <termios.h> -#endif -#undef CEOF /* syntax.h redefines this */ -#endif -#include "redir.h" -#include "show.h" -#include "main.h" -#include "parser.h" -#include "nodes.h" -#include "jobs.h" -#include "options.h" -#include "trap.h" -#include "syntax.h" -#include "input.h" -#include "output.h" -#include "memalloc.h" -#include "error.h" -#include "mystring.h" - -// Use of process groups is disabled to allow adb shell children to terminate when the shell dies -#define USE_PROCESS_GROUPS - - -static struct job *jobtab; /* array of jobs */ -static int njobs; /* size of array */ -static int jobs_invalid; /* set in child */ -MKINIT pid_t backgndpid = -1; /* pid of last background process */ -#if JOBS -int initialpgrp; /* pgrp of shell on invocation */ -static int curjob = -1; /* current job */ -#endif -static int ttyfd = -1; - -STATIC void restartjob(struct job *); -STATIC void freejob(struct job *); -STATIC struct job *getjob(const char *, int); -STATIC int dowait(int, struct job *); -STATIC int onsigchild(void); -STATIC int waitproc(int, struct job *, int *); -STATIC void cmdtxt(union node *); -STATIC void cmdlist(union node *, int); -STATIC void cmdputs(const char *); - -#ifdef OLD_TTY_DRIVER -static pid_t tcgetpgrp(int fd); -static int tcsetpgrp(int fd, pid_t pgrp); - -static pid_t -tcgetpgrp(int fd) -{ - pid_t pgrp; - if (ioctl(fd, TIOCGPGRP, (char *)&pgrp) == -1) - return -1; - else - return pgrp; -} - -static int -tcsetpgrp(int fd, pid_tpgrp) -{ - return ioctl(fd, TIOCSPGRP, (char *)&pgrp); -} -#endif - -/* - * Turn job control on and off. - * - * Note: This code assumes that the third arg to ioctl is a character - * pointer, which is true on Berkeley systems but not System V. Since - * System V doesn't have job control yet, this isn't a problem now. - */ - -MKINIT int jobctl; - -void -setjobctl(int on) -{ -#ifdef OLD_TTY_DRIVER - int ldisc; -#endif - - if (on == jobctl || rootshell == 0) - return; - if (on) { -#if defined(FIOCLEX) || defined(FD_CLOEXEC) - int err; - int i; - if (ttyfd != -1) - close(ttyfd); - if ((ttyfd = open("/dev/tty", O_RDWR)) == -1) { - for (i = 0; i < 3; i++) { - if (isatty(i) && (ttyfd = dup(i)) != -1) - break; - } - if (i == 3) - goto out; - } - /* Move to a high fd */ - for (i = 10; i > 2; i--) { - if ((err = fcntl(ttyfd, F_DUPFD, (1 << i) - 1)) != -1) - break; - } - if (err != -1) { - close(ttyfd); - ttyfd = err; - } -#ifdef FIOCLEX - err = ioctl(ttyfd, FIOCLEX, 0); -#elif FD_CLOEXEC - err = fcntl(ttyfd, F_SETFD, - fcntl(ttyfd, F_GETFD, 0) | FD_CLOEXEC); -#endif - if (err == -1) { - close(ttyfd); - ttyfd = -1; - goto out; - } -#else - out2str("sh: Need FIOCLEX or FD_CLOEXEC to support job control"); - goto out; -#endif - do { /* while we are in the background */ - if ((initialpgrp = tcgetpgrp(ttyfd)) < 0) { -out: - out2str("sh: can't access tty; job control turned off\n"); - mflag = 0; - return; - } - if (initialpgrp == -1) - initialpgrp = getpgrp(); - else if (initialpgrp != getpgrp()) { - killpg(0, SIGTTIN); - continue; - } - } while (0); - -#ifdef OLD_TTY_DRIVER - if (ioctl(ttyfd, TIOCGETD, (char *)&ldisc) < 0 - || ldisc != NTTYDISC) { - out2str("sh: need new tty driver to run job control; job control turned off\n"); - mflag = 0; - return; - } -#endif - setsignal(SIGTSTP, 0); - setsignal(SIGTTOU, 0); - setsignal(SIGTTIN, 0); -#ifdef USE_PROCESS_GROUPS - if (getpgid(0) != rootpid && setpgid(0, rootpid) == -1) - error("Cannot set process group (%s) at %d", - strerror(errno), __LINE__); - if (tcsetpgrp(ttyfd, rootpid) == -1) - error("Cannot set tty process group (%s) at %d", - strerror(errno), __LINE__); -#endif - } else { /* turning job control off */ -#ifdef USE_PROCESS_GROUPS - if (getpgid(0) != initialpgrp && setpgid(0, initialpgrp) == -1) - error("Cannot set process group (%s) at %d", - strerror(errno), __LINE__); - if (tcsetpgrp(ttyfd, initialpgrp) == -1) - error("Cannot set tty process group (%s) at %d", - strerror(errno), __LINE__); -#endif - close(ttyfd); - ttyfd = -1; - setsignal(SIGTSTP, 0); - setsignal(SIGTTOU, 0); - setsignal(SIGTTIN, 0); - } - jobctl = on; -} - - -#ifdef mkinit -INCLUDE <stdlib.h> - -SHELLPROC { - backgndpid = -1; -#if JOBS - jobctl = 0; -#endif -} - -#endif - - - -#if JOBS -int -fgcmd(int argc, char **argv) -{ - struct job *jp; - int i; - int status; - - nextopt(""); - jp = getjob(*argptr, 0); - if (jp->jobctl == 0) - error("job not created under job control"); - out1fmt("%s", jp->ps[0].cmd); - for (i = 1; i < jp->nprocs; i++) - out1fmt(" | %s", jp->ps[i].cmd ); - out1c('\n'); - flushall(); - - for (i = 0; i < jp->nprocs; i++) - if (tcsetpgrp(ttyfd, jp->ps[i].pid) != -1) - break; - - if (i >= jp->nprocs) { - error("Cannot set tty process group (%s) at %d", - strerror(errno), __LINE__); - } - restartjob(jp); - INTOFF; - status = waitforjob(jp); - INTON; - return status; -} - -static void -set_curjob(struct job *jp, int mode) -{ - struct job *jp1, *jp2; - int i, ji; - - ji = jp - jobtab; - - /* first remove from list */ - if (ji == curjob) - curjob = jp->prev_job; - else { - for (i = 0; i < njobs; i++) { - if (jobtab[i].prev_job != ji) - continue; - jobtab[i].prev_job = jp->prev_job; - break; - } - } - - /* Then re-insert in correct position */ - switch (mode) { - case 0: /* job being deleted */ - jp->prev_job = -1; - break; - case 1: /* newly created job or backgrounded job, - put after all stopped jobs. */ - if (curjob != -1 && jobtab[curjob].state == JOBSTOPPED) { - for (jp1 = jobtab + curjob; ; jp1 = jp2) { - if (jp1->prev_job == -1) - break; - jp2 = jobtab + jp1->prev_job; - if (jp2->state != JOBSTOPPED) - break; - } - jp->prev_job = jp1->prev_job; - jp1->prev_job = ji; - break; - } - /* FALLTHROUGH */ - case 2: /* newly stopped job - becomes curjob */ - jp->prev_job = curjob; - curjob = ji; - break; - } -} - -int -bgcmd(int argc, char **argv) -{ - struct job *jp; - int i; - - nextopt(""); - do { - jp = getjob(*argptr, 0); - if (jp->jobctl == 0) - error("job not created under job control"); - set_curjob(jp, 1); - out1fmt("[%ld] %s", (long)(jp - jobtab + 1), jp->ps[0].cmd); - for (i = 1; i < jp->nprocs; i++) - out1fmt(" | %s", jp->ps[i].cmd ); - out1c('\n'); - flushall(); - restartjob(jp); - } while (*argptr && *++argptr); - return 0; -} - - -STATIC void -restartjob(struct job *jp) -{ - struct procstat *ps; - int i; - - if (jp->state == JOBDONE) - return; - INTOFF; - for (i = 0; i < jp->nprocs; i++) - if (killpg(jp->ps[i].pid, SIGCONT) != -1) - break; - if (i >= jp->nprocs) - error("Cannot continue job (%s)", strerror(errno)); - for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) { - if (WIFSTOPPED(ps->status)) { - ps->status = -1; - jp->state = JOBRUNNING; - } - } - INTON; -} -#endif - -static void -showjob(struct output *out, struct job *jp, int mode) -{ - int procno; - int st; - struct procstat *ps; - int col; - char s[64]; - -#if JOBS - if (mode & SHOW_PGID) { - /* just output process (group) id of pipeline */ - outfmt(out, "%ld\n", (long)jp->ps->pid); - return; - } -#endif - - procno = jp->nprocs; - if (!procno) - return; - - if (mode & SHOW_PID) - mode |= SHOW_MULTILINE; - - if ((procno > 1 && !(mode & SHOW_MULTILINE)) - || (mode & SHOW_SIGNALLED)) { - /* See if we have more than one status to report */ - ps = jp->ps; - st = ps->status; - do { - int st1 = ps->status; - if (st1 != st) - /* yes - need multi-line output */ - mode |= SHOW_MULTILINE; - if (st1 == -1 || !(mode & SHOW_SIGNALLED) || WIFEXITED(st1)) - continue; - if (WIFSTOPPED(st1) || ((st1 = WTERMSIG(st1) & 0x7f) - && st1 != SIGINT && st1 != SIGPIPE)) - mode |= SHOW_ISSIG; - - } while (ps++, --procno); - procno = jp->nprocs; - } - - if (mode & SHOW_SIGNALLED && !(mode & SHOW_ISSIG)) { - if (jp->state == JOBDONE && !(mode & SHOW_NO_FREE)) { - TRACE(("showjob: freeing job %d\n", jp - jobtab + 1)); - freejob(jp); - } - return; - } - - for (ps = jp->ps; --procno >= 0; ps++) { /* for each process */ - if (ps == jp->ps) - fmtstr(s, 16, "[%ld] %c ", - (long)(jp - jobtab + 1), -#if JOBS - jp == jobtab + curjob ? '+' : - curjob != -1 && jp == jobtab + - jobtab[curjob].prev_job ? '-' : -#endif - ' '); - else - fmtstr(s, 16, " " ); - col = strlen(s); - if (mode & SHOW_PID) { - fmtstr(s + col, 16, "%ld ", (long)ps->pid); - col += strlen(s + col); - } - if (ps->status == -1) { - scopy("Running", s + col); - } else if (WIFEXITED(ps->status)) { - st = WEXITSTATUS(ps->status); - if (st) - fmtstr(s + col, 16, "Done(%d)", st); - else - fmtstr(s + col, 16, "Done"); - } else { -#if JOBS - if (WIFSTOPPED(ps->status)) - st = WSTOPSIG(ps->status); - else /* WIFSIGNALED(ps->status) */ -#endif - st = WTERMSIG(ps->status); - st &= 0x7f; - if (st < NSIG && sys_siglist[st]) - scopyn(sys_siglist[st], s + col, 32); - else - fmtstr(s + col, 16, "Signal %d", st); - if (WCOREDUMP(ps->status)) { - col += strlen(s + col); - scopyn(" (core dumped)", s + col, 64 - col); - } - } - col += strlen(s + col); - outstr(s, out); - do { - outc(' ', out); - col++; - } while (col < 30); - outstr(ps->cmd, out); - if (mode & SHOW_MULTILINE) { - if (procno > 0) { - outc(' ', out); - outc('|', out); - } - } else { - while (--procno >= 0) - outfmt(out, " | %s", (++ps)->cmd ); - } - outc('\n', out); - } - flushout(out); - jp->changed = 0; - if (jp->state == JOBDONE && !(mode & SHOW_NO_FREE)) - freejob(jp); -} - - -int -jobscmd(int argc, char **argv) -{ - int mode, m; - int sv = jobs_invalid; - - jobs_invalid = 0; - mode = 0; - while ((m = nextopt("lp"))) - if (m == 'l') - mode = SHOW_PID; - else - mode = SHOW_PGID; - if (*argptr) - do - showjob(out1, getjob(*argptr,0), mode); - while (*++argptr); - else - showjobs(out1, mode); - jobs_invalid = sv; - return 0; -} - - -/* - * Print a list of jobs. If "change" is nonzero, only print jobs whose - * statuses have changed since the last call to showjobs. - * - * If the shell is interrupted in the process of creating a job, the - * result may be a job structure containing zero processes. Such structures - * will be freed here. - */ - -void -showjobs(struct output *out, int mode) -{ - int jobno; - struct job *jp; - int silent = 0, gotpid; - - TRACE(("showjobs(%x) called\n", mode)); - - /* If not even one one job changed, there is nothing to do */ - gotpid = dowait(0, NULL); - while (dowait(0, NULL) > 0) - continue; -#ifdef JOBS - /* - * Check if we are not in our foreground group, and if not - * put us in it. - */ - if (mflag && gotpid != -1 && tcgetpgrp(ttyfd) != getpid()) { - if (tcsetpgrp(ttyfd, getpid()) == -1) - error("Cannot set tty process group (%s) at %d", - strerror(errno), __LINE__); - TRACE(("repaired tty process group\n")); - silent = 1; - } -#endif - if (jobs_invalid) - return; - - for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) { - if (!jp->used) - continue; - if (jp->nprocs == 0) { - freejob(jp); - continue; - } - if ((mode & SHOW_CHANGED) && !jp->changed) - continue; - if (silent && jp->changed) { - jp->changed = 0; - continue; - } - showjob(out, jp, mode); - } -} - -/* - * Mark a job structure as unused. - */ - -STATIC void -freejob(struct job *jp) -{ - INTOFF; - if (jp->ps != &jp->ps0) { - ckfree(jp->ps); - jp->ps = &jp->ps0; - } - jp->nprocs = 0; - jp->used = 0; -#if JOBS - set_curjob(jp, 0); -#endif - INTON; -} - - - -int -waitcmd(int argc, char **argv) -{ - struct job *job; - int status, retval = 127; - struct job *jp; - - nextopt(""); - - if (!*argptr) { - /* wait for all jobs */ - jp = jobtab; - if (jobs_invalid) - return 0; - for (;;) { - if (jp >= jobtab + njobs) { - /* no running procs */ - return 0; - } - if (!jp->used || jp->state != JOBRUNNING) { - jp++; - continue; - } - if (dowait(1, (struct job *)NULL) == -1) - return 128 + SIGINT; - jp = jobtab; - } - } - - for (; *argptr; argptr++) { - job = getjob(*argptr, 1); - if (!job) { - retval = 127; - continue; - } - /* loop until process terminated or stopped */ - while (job->state == JOBRUNNING) { - if (dowait(1, (struct job *)NULL) == -1) - return 128 + SIGINT; - } - status = job->ps[job->nprocs].status; - if (WIFEXITED(status)) - retval = WEXITSTATUS(status); -#if JOBS - else if (WIFSTOPPED(status)) - retval = WSTOPSIG(status) + 128; -#endif - else { - /* XXX: limits number of signals */ - retval = WTERMSIG(status) + 128; - } - if (!iflag) - freejob(job); - } - return retval; -} - - - -int -jobidcmd(int argc, char **argv) -{ - struct job *jp; - int i; - - nextopt(""); - jp = getjob(*argptr, 0); - for (i = 0 ; i < jp->nprocs ; ) { - out1fmt("%ld", (long)jp->ps[i].pid); - out1c(++i < jp->nprocs ? ' ' : '\n'); - } - return 0; -} - -int -getjobpgrp(const char *name) -{ - struct job *jp; - - jp = getjob(name, 1); - if (jp == 0) - return 0; - return -jp->ps[0].pid; -} - -/* - * Convert a job name to a job structure. - */ - -STATIC struct job * -getjob(const char *name, int noerror) -{ - int jobno = -1; - struct job *jp; - int pid; - int i; - const char *err_msg = "No such job: %s"; - - if (name == NULL) { -#if JOBS - jobno = curjob; -#endif - err_msg = "No current job"; - } else if (name[0] == '%') { - if (is_number(name + 1)) { - jobno = number(name + 1) - 1; - } else if (!name[2]) { - switch (name[1]) { -#if JOBS - case 0: - case '+': - case '%': - jobno = curjob; - err_msg = "No current job"; - break; - case '-': - jobno = curjob; - if (jobno != -1) - jobno = jobtab[jobno].prev_job; - err_msg = "No previous job"; - break; -#endif - default: - goto check_pattern; - } - } else { - struct job *found; - check_pattern: - found = NULL; - for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { - if (!jp->used || jp->nprocs <= 0) - continue; - if ((name[1] == '?' - && strstr(jp->ps[0].cmd, name + 2)) - || prefix(name + 1, jp->ps[0].cmd)) { - if (found) { - err_msg = "%s: ambiguous"; - found = 0; - break; - } - found = jp; - } - } - if (found) - return found; - } - - } else if (is_number(name)) { - pid = number(name); - for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { - if (jp->used && jp->nprocs > 0 - && jp->ps[jp->nprocs - 1].pid == pid) - return jp; - } - } - - if (!jobs_invalid && jobno >= 0 && jobno < njobs) { - jp = jobtab + jobno; - if (jp->used) - return jp; - } - if (!noerror) - error(err_msg, name); - return 0; -} - - - -/* - * Return a new job structure, - */ - -struct job * -makejob(union node *node, int nprocs) -{ - int i; - struct job *jp; - - if (jobs_invalid) { - for (i = njobs, jp = jobtab ; --i >= 0 ; jp++) { - if (jp->used) - freejob(jp); - } - jobs_invalid = 0; - } - - for (i = njobs, jp = jobtab ; ; jp++) { - if (--i < 0) { - INTOFF; - if (njobs == 0) { - jobtab = ckmalloc(4 * sizeof jobtab[0]); - } else { - jp = ckmalloc((njobs + 4) * sizeof jobtab[0]); - memcpy(jp, jobtab, njobs * sizeof jp[0]); - /* Relocate `ps' pointers */ - for (i = 0; i < njobs; i++) - if (jp[i].ps == &jobtab[i].ps0) - jp[i].ps = &jp[i].ps0; - ckfree(jobtab); - jobtab = jp; - } - jp = jobtab + njobs; - for (i = 4 ; --i >= 0 ; jobtab[njobs++].used = 0); - INTON; - break; - } - if (jp->used == 0) - break; - } - INTOFF; - jp->state = JOBRUNNING; - jp->used = 1; - jp->changed = 0; - jp->nprocs = 0; -#if JOBS - jp->jobctl = jobctl; - set_curjob(jp, 1); -#endif - if (nprocs > 1) { - jp->ps = ckmalloc(nprocs * sizeof (struct procstat)); - } else { - jp->ps = &jp->ps0; - } - INTON; - TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs, - jp - jobtab + 1)); - return jp; -} - - -/* - * Fork off a subshell. If we are doing job control, give the subshell its - * own process group. Jp is a job structure that the job is to be added to. - * N is the command that will be evaluated by the child. Both jp and n may - * be NULL. The mode parameter can be one of the following: - * FORK_FG - Fork off a foreground process. - * FORK_BG - Fork off a background process. - * FORK_NOJOB - Like FORK_FG, but don't give the process its own - * process group even if job control is on. - * - * When job control is turned off, background processes have their standard - * input redirected to /dev/null (except for the second and later processes - * in a pipeline). - */ - -int -forkshell(struct job *jp, union node *n, int mode) -{ - int pid; - - TRACE(("forkshell(%%%d, %p, %d) called\n", jp - jobtab, n, mode)); - switch ((pid = fork())) { - case -1: - TRACE(("Fork failed, errno=%d\n", errno)); - INTON; - error("Cannot fork"); - break; - case 0: - forkchild(jp, n, mode, 0); - return 0; - default: - return forkparent(jp, n, mode, pid); - } -} - -int -forkparent(struct job *jp, union node *n, int mode, pid_t pid) -{ - int pgrp; - - if (rootshell && mode != FORK_NOJOB && mflag) { - if (jp == NULL || jp->nprocs == 0) - pgrp = pid; - else - pgrp = jp->ps[0].pid; -#ifdef USE_PROCESS_GROUPS - /* This can fail because we are doing it in the child also */ - (void)setpgid(pid, pgrp); -#endif - } - if (mode == FORK_BG) - backgndpid = pid; /* set $! */ - if (jp) { - struct procstat *ps = &jp->ps[jp->nprocs++]; - ps->pid = pid; - ps->status = -1; - ps->cmd[0] = 0; - if (/* iflag && rootshell && */ n) - commandtext(ps, n); - } - TRACE(("In parent shell: child = %d\n", pid)); - return pid; -} - -void -forkchild(struct job *jp, union node *n, int mode, int vforked) -{ - int wasroot; - int pgrp; - const char *devnull = _PATH_DEVNULL; - const char *nullerr = "Can't open %s"; - - wasroot = rootshell; - TRACE(("Child shell %d\n", getpid())); - if (!vforked) - rootshell = 0; - - closescript(vforked); - clear_traps(vforked); -#if JOBS - if (!vforked) - jobctl = 0; /* do job control only in root shell */ - if (wasroot && mode != FORK_NOJOB && mflag) { - if (jp == NULL || jp->nprocs == 0) - pgrp = getpid(); - else - pgrp = jp->ps[0].pid; -#ifdef USE_PROCESS_GROUPS - /* This can fail because we are doing it in the parent also */ - (void)setpgid(0, pgrp); - if (mode == FORK_FG) { - if (tcsetpgrp(ttyfd, pgrp) == -1) - error("Cannot set tty process group (%s) at %d", - strerror(errno), __LINE__); - } -#endif - setsignal(SIGTSTP, vforked); - setsignal(SIGTTOU, vforked); - } else if (mode == FORK_BG) { - ignoresig(SIGINT, vforked); - ignoresig(SIGQUIT, vforked); - if ((jp == NULL || jp->nprocs == 0) && - ! fd0_redirected_p ()) { - close(0); - if (open(devnull, O_RDONLY) != 0) - error(nullerr, devnull); - } - } -#else - if (mode == FORK_BG) { - ignoresig(SIGINT, vforked); - ignoresig(SIGQUIT, vforked); - if ((jp == NULL || jp->nprocs == 0) && - ! fd0_redirected_p ()) { - close(0); - if (open(devnull, O_RDONLY) != 0) - error(nullerr, devnull); - } - } -#endif - if (wasroot && iflag) { - setsignal(SIGINT, vforked); - setsignal(SIGQUIT, vforked); - setsignal(SIGTERM, vforked); - } - - if (!vforked) - jobs_invalid = 1; -} - -/* - * Wait for job to finish. - * - * Under job control we have the problem that while a child process is - * running interrupts generated by the user are sent to the child but not - * to the shell. This means that an infinite loop started by an inter- - * active user may be hard to kill. With job control turned off, an - * interactive user may place an interactive program inside a loop. If - * the interactive program catches interrupts, the user doesn't want - * these interrupts to also abort the loop. The approach we take here - * is to have the shell ignore interrupt signals while waiting for a - * forground process to terminate, and then send itself an interrupt - * signal if the child process was terminated by an interrupt signal. - * Unfortunately, some programs want to do a bit of cleanup and then - * exit on interrupt; unless these processes terminate themselves by - * sending a signal to themselves (instead of calling exit) they will - * confuse this approach. - */ - -int -waitforjob(struct job *jp) -{ -#if JOBS - int mypgrp = getpgrp(); -#endif - int status; - int st; - - INTOFF; - TRACE(("waitforjob(%%%d) called\n", jp - jobtab + 1)); - while (jp->state == JOBRUNNING) { - dowait(1, jp); - } -#if JOBS - if (jp->jobctl) { - if (tcsetpgrp(ttyfd, mypgrp) == -1) - error("Cannot set tty process group (%s) at %d", - strerror(errno), __LINE__); - } - if (jp->state == JOBSTOPPED && curjob != jp - jobtab) - set_curjob(jp, 2); -#endif - status = jp->ps[jp->nprocs - 1].status; - /* convert to 8 bits */ - if (WIFEXITED(status)) - st = WEXITSTATUS(status); -#if JOBS - else if (WIFSTOPPED(status)) - st = WSTOPSIG(status) + 128; -#endif - else - st = WTERMSIG(status) + 128; - TRACE(("waitforjob: job %d, nproc %d, status %x, st %x\n", - jp - jobtab + 1, jp->nprocs, status, st )); -#if JOBS - if (jp->jobctl) { - /* - * This is truly gross. - * If we're doing job control, then we did a TIOCSPGRP which - * caused us (the shell) to no longer be in the controlling - * session -- so we wouldn't have seen any ^C/SIGINT. So, we - * intuit from the subprocess exit status whether a SIGINT - * occurred, and if so interrupt ourselves. Yuck. - mycroft - */ - if (WIFSIGNALED(status) && WTERMSIG(status) == SIGINT) - raise(SIGINT); - } -#endif - if (! JOBS || jp->state == JOBDONE) - freejob(jp); - INTON; - return st; -} - - - -/* - * Wait for a process to terminate. - */ - -STATIC int -dowait(int block, struct job *job) -{ - int pid; - int status; - struct procstat *sp; - struct job *jp; - struct job *thisjob; - int done; - int stopped; - extern volatile char gotsig[]; - - TRACE(("dowait(%d) called\n", block)); - do { - pid = waitproc(block, job, &status); - TRACE(("wait returns pid %d, status %d\n", pid, status)); - } while (pid == -1 && errno == EINTR && gotsig[SIGINT - 1] == 0); - if (pid <= 0) - return pid; - INTOFF; - thisjob = NULL; - for (jp = jobtab ; jp < jobtab + njobs ; jp++) { - if (jp->used) { - done = 1; - stopped = 1; - for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) { - if (sp->pid == -1) - continue; - if (sp->pid == pid) { - TRACE(("Job %d: changing status of proc %d from 0x%x to 0x%x\n", jp - jobtab + 1, pid, sp->status, status)); - sp->status = status; - thisjob = jp; - } - if (sp->status == -1) - stopped = 0; - else if (WIFSTOPPED(sp->status)) - done = 0; - } - if (stopped) { /* stopped or done */ - int state = done ? JOBDONE : JOBSTOPPED; - if (jp->state != state) { - TRACE(("Job %d: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state)); - jp->state = state; -#if JOBS - if (done) - set_curjob(jp, 0); -#endif - } - } - } - } - - if (thisjob && thisjob->state != JOBRUNNING) { - int mode = 0; - if (!rootshell || !iflag) - mode = SHOW_SIGNALLED; - if (job == thisjob) - mode = SHOW_SIGNALLED | SHOW_NO_FREE; - if (mode) - showjob(out2, thisjob, mode); - else { - TRACE(("Not printing status, rootshell=%d, job=%p\n", - rootshell, job)); - thisjob->changed = 1; - } - } - - INTON; - return pid; -} - - - -/* - * Do a wait system call. If job control is compiled in, we accept - * stopped processes. If block is zero, we return a value of zero - * rather than blocking. - * - * System V doesn't have a non-blocking wait system call. It does - * have a SIGCLD signal that is sent to a process when one of it's - * children dies. The obvious way to use SIGCLD would be to install - * a handler for SIGCLD which simply bumped a counter when a SIGCLD - * was received, and have waitproc bump another counter when it got - * the status of a process. Waitproc would then know that a wait - * system call would not block if the two counters were different. - * This approach doesn't work because if a process has children that - * have not been waited for, System V will send it a SIGCLD when it - * installs a signal handler for SIGCLD. What this means is that when - * a child exits, the shell will be sent SIGCLD signals continuously - * until is runs out of stack space, unless it does a wait call before - * restoring the signal handler. The code below takes advantage of - * this (mis)feature by installing a signal handler for SIGCLD and - * then checking to see whether it was called. If there are any - * children to be waited for, it will be. - * - * If neither SYSV nor BSD is defined, we don't implement nonblocking - * waits at all. In this case, the user will not be informed when - * a background process until the next time she runs a real program - * (as opposed to running a builtin command or just typing return), - * and the jobs command may give out of date information. - */ - -#ifdef SYSV -STATIC int gotsigchild; - -STATIC int onsigchild() { - gotsigchild = 1; -} -#endif - - -STATIC int -waitproc(int block, struct job *jp, int *status) -{ -#ifdef BSD - int flags = 0; - -#if JOBS - if (jp != NULL && jp->jobctl) - flags |= WUNTRACED; -#endif - if (block == 0) - flags |= WNOHANG; - return wait3(status, flags, (struct rusage *)NULL); -#else -#ifdef SYSV - int (*save)(); - - if (block == 0) { - gotsigchild = 0; - save = signal(SIGCLD, onsigchild); - signal(SIGCLD, save); - if (gotsigchild == 0) - return 0; - } - return wait(status); -#else - if (block == 0) - return 0; - return wait(status); -#endif -#endif -} - -/* - * return 1 if there are stopped jobs, otherwise 0 - */ -int job_warning = 0; -int -stoppedjobs(void) -{ - int jobno; - struct job *jp; - - if (job_warning || jobs_invalid) - return (0); - for (jobno = 1, jp = jobtab; jobno <= njobs; jobno++, jp++) { - if (jp->used == 0) - continue; - if (jp->state == JOBSTOPPED) { - out2str("You have stopped jobs.\n"); - job_warning = 2; - return (1); - } - } - - return (0); -} - -/* - * Return a string identifying a command (to be printed by the - * jobs command). - */ - -STATIC char *cmdnextc; -STATIC int cmdnleft; - -void -commandtext(struct procstat *ps, union node *n) -{ - int len; - - cmdnextc = ps->cmd; - if (iflag || mflag || sizeof ps->cmd < 100) - len = sizeof(ps->cmd); - else - len = sizeof(ps->cmd) / 10; - cmdnleft = len; - cmdtxt(n); - if (cmdnleft <= 0) { - char *p = ps->cmd + len - 4; - p[0] = '.'; - p[1] = '.'; - p[2] = '.'; - p[3] = 0; - } else - *cmdnextc = '\0'; - TRACE(("commandtext: ps->cmd %x, end %x, left %d\n\t\"%s\"\n", - ps->cmd, cmdnextc, cmdnleft, ps->cmd)); -} - - -STATIC void -cmdtxt(union node *n) -{ - union node *np; - struct nodelist *lp; - const char *p; - int i; - char s[2]; - - if (n == NULL || cmdnleft <= 0) - return; - switch (n->type) { - case NSEMI: - cmdtxt(n->nbinary.ch1); - cmdputs("; "); - cmdtxt(n->nbinary.ch2); - break; - case NAND: - cmdtxt(n->nbinary.ch1); - cmdputs(" && "); - cmdtxt(n->nbinary.ch2); - break; - case NOR: - cmdtxt(n->nbinary.ch1); - cmdputs(" || "); - cmdtxt(n->nbinary.ch2); - break; - case NPIPE: - for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { - cmdtxt(lp->n); - if (lp->next) - cmdputs(" | "); - } - break; - case NSUBSHELL: - cmdputs("("); - cmdtxt(n->nredir.n); - cmdputs(")"); - break; - case NREDIR: - case NBACKGND: - cmdtxt(n->nredir.n); - break; - case NIF: - cmdputs("if "); - cmdtxt(n->nif.test); - cmdputs("; then "); - cmdtxt(n->nif.ifpart); - if (n->nif.elsepart) { - cmdputs("; else "); - cmdtxt(n->nif.elsepart); - } - cmdputs("; fi"); - break; - case NWHILE: - cmdputs("while "); - goto until; - case NUNTIL: - cmdputs("until "); -until: - cmdtxt(n->nbinary.ch1); - cmdputs("; do "); - cmdtxt(n->nbinary.ch2); - cmdputs("; done"); - break; - case NFOR: - cmdputs("for "); - cmdputs(n->nfor.var); - cmdputs(" in "); - cmdlist(n->nfor.args, 1); - cmdputs("; do "); - cmdtxt(n->nfor.body); - cmdputs("; done"); - break; - case NCASE: - cmdputs("case "); - cmdputs(n->ncase.expr->narg.text); - cmdputs(" in "); - for (np = n->ncase.cases; np; np = np->nclist.next) { - cmdtxt(np->nclist.pattern); - cmdputs(") "); - cmdtxt(np->nclist.body); - cmdputs(";; "); - } - cmdputs("esac"); - break; - case NDEFUN: - cmdputs(n->narg.text); - cmdputs("() { ... }"); - break; - case NCMD: - cmdlist(n->ncmd.args, 1); - cmdlist(n->ncmd.redirect, 0); - break; - case NARG: - cmdputs(n->narg.text); - break; - case NTO: - p = ">"; i = 1; goto redir; - case NCLOBBER: - p = ">|"; i = 1; goto redir; - case NAPPEND: - p = ">>"; i = 1; goto redir; - case NTOFD: - p = ">&"; i = 1; goto redir; - case NFROM: - p = "<"; i = 0; goto redir; - case NFROMFD: - p = "<&"; i = 0; goto redir; - case NFROMTO: - p = "<>"; i = 0; goto redir; -redir: - if (n->nfile.fd != i) { - s[0] = n->nfile.fd + '0'; - s[1] = '\0'; - cmdputs(s); - } - cmdputs(p); - if (n->type == NTOFD || n->type == NFROMFD) { - s[0] = n->ndup.dupfd + '0'; - s[1] = '\0'; - cmdputs(s); - } else { - cmdtxt(n->nfile.fname); - } - break; - case NHERE: - case NXHERE: - cmdputs("<<..."); - break; - default: - cmdputs("???"); - break; - } -} - -STATIC void -cmdlist(union node *np, int sep) -{ - for (; np; np = np->narg.next) { - if (!sep) - cmdputs(" "); - cmdtxt(np); - if (sep && np->narg.next) - cmdputs(" "); - } -} - - -STATIC void -cmdputs(const char *s) -{ - const char *p, *str = 0; - char c, cc[2] = " "; - char *nextc; - int nleft; - int subtype = 0; - int quoted = 0; - static char vstype[16][4] = { "", "}", "-", "+", "?", "=", - "#", "##", "%", "%%" }; - - p = s; - nextc = cmdnextc; - nleft = cmdnleft; - while (nleft > 0 && (c = *p++) != 0) { - switch (c) { - case CTLESC: - c = *p++; - break; - case CTLVAR: - subtype = *p++; - if ((subtype & VSTYPE) == VSLENGTH) - str = "${#"; - else - str = "${"; - if (!(subtype & VSQUOTE) != !(quoted & 1)) { - quoted ^= 1; - c = '"'; - } else - c = *str++; - break; - case CTLENDVAR: - if (quoted & 1) { - c = '"'; - str = "}"; - } else - c = '}'; - quoted >>= 1; - subtype = 0; - break; - case CTLBACKQ: - c = '$'; - str = "(...)"; - break; - case CTLBACKQ+CTLQUOTE: - c = '"'; - str = "$(...)\""; - break; - case CTLARI: - c = '$'; - str = "(("; - break; - case CTLENDARI: - c = ')'; - str = ")"; - break; - case CTLQUOTEMARK: - quoted ^= 1; - c = '"'; - break; - case '=': - if (subtype == 0) - break; - str = vstype[subtype & VSTYPE]; - if (subtype & VSNUL) - c = ':'; - else - c = *str++; - if (c != '}') - quoted <<= 1; - break; - case '\'': - case '\\': - case '"': - case '$': - /* These can only happen inside quotes */ - cc[0] = c; - str = cc; - c = '\\'; - break; - default: - break; - } - do { - *nextc++ = c; - } while (--nleft > 0 && str && (c = *str++)); - str = 0; - } - if ((quoted & 1) && nleft) { - *nextc++ = '"'; - nleft--; - } - cmdnleft = nleft; - cmdnextc = nextc; -} diff --git a/sh/jobs.h b/sh/jobs.h deleted file mode 100644 index 47e76c2..0000000 --- a/sh/jobs.h +++ /dev/null @@ -1,106 +0,0 @@ -/* $NetBSD: jobs.h,v 1.19 2003/11/27 21:16:14 dsl 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 - * Kenneth Almquist. - * - * 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. - * - * @(#)jobs.h 8.2 (Berkeley) 5/4/95 - */ - -#include "output.h" - -/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ -#define FORK_FG 0 -#define FORK_BG 1 -#define FORK_NOJOB 2 - -/* mode flags for showjob(s) */ -#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */ -#define SHOW_MULTILINE 0x02 /* one line per process */ -#define SHOW_PID 0x04 /* include process pid */ -#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */ -#define SHOW_SIGNALLED 0x10 /* only if stopped/exited on signal */ -#define SHOW_ISSIG 0x20 /* job was signalled */ -#define SHOW_NO_FREE 0x40 /* do not free job */ - - -/* - * A job structure contains information about a job. A job is either a - * single process or a set of processes contained in a pipeline. In the - * latter case, pidlist will be non-NULL, and will point to a -1 terminated - * array of pids. - */ -#define MAXCMDTEXT 200 - -struct procstat { - pid_t pid; /* process id */ - int status; /* last process status from wait() */ - char cmd[MAXCMDTEXT];/* text of command being run */ -}; - -struct job { - struct procstat ps0; /* status of process */ - struct procstat *ps; /* status or processes when more than one */ - int nprocs; /* number of processes */ - pid_t pgrp; /* process group of this job */ - char state; -#define JOBRUNNING 0 /* at least one proc running */ -#define JOBSTOPPED 1 /* all procs are stopped */ -#define JOBDONE 2 /* all procs are completed */ - char used; /* true if this entry is in used */ - char changed; /* true if status has changed */ -#if JOBS - char jobctl; /* job running under job control */ - int prev_job; /* previous job index */ -#endif -}; - -extern pid_t backgndpid; /* pid of last background process */ -extern int job_warning; /* user was warned about stopped jobs */ - -void setjobctl(int); -int fgcmd(int, char **); -int bgcmd(int, char **); -int jobscmd(int, char **); -void showjobs(struct output *, int); -int waitcmd(int, char **); -int jobidcmd(int, char **); -struct job *makejob(union node *, int); -int forkshell(struct job *, union node *, int); -void forkchild(struct job *, union node *, int, int); -int forkparent(struct job *, union node *, int, pid_t); -int waitforjob(struct job *); -int stoppedjobs(void); -void commandtext(struct procstat *, union node *); -int getjobpgrp(const char *); - -#if ! JOBS -#define setjobctl(on) /* do nothing */ -#endif diff --git a/sh/machdep.h b/sh/machdep.h deleted file mode 100644 index 14e803b..0000000 --- a/sh/machdep.h +++ /dev/null @@ -1,47 +0,0 @@ -/* $NetBSD: machdep.h,v 1.11 2003/08/07 09:05:33 agc 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 - * Kenneth Almquist. - * - * 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. - * - * @(#)machdep.h 8.2 (Berkeley) 5/4/95 - */ - -/* - * Most machines require the value returned from malloc to be aligned - * in some way. The following macro will get this right on many machines. - */ - -#define SHELL_SIZE (sizeof(union {int i; char *cp; double d; }) - 1) -/* - * It appears that grabstackstr() will barf with such alignments - * because stalloc() will return a string allocated in a new stackblock. - */ -#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE) diff --git a/sh/main.c b/sh/main.c deleted file mode 100644 index 43b154f..0000000 --- a/sh/main.c +++ /dev/null @@ -1,394 +0,0 @@ -/* $NetBSD: main.c,v 1.48 2003/09/14 12:09:29 jmmv 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 - * Kenneth Almquist. - * - * 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) 1991, 1993\n\ - The Regents of the University of California. All rights reserved.\n"); -#endif /* not lint */ - -#ifndef lint -#if 0 -static char sccsid[] = "@(#)main.c 8.7 (Berkeley) 7/19/95"; -#else -__RCSID("$NetBSD: main.c,v 1.48 2003/09/14 12:09:29 jmmv Exp $"); -#endif -#endif /* not lint */ - -#include <errno.h> -#include <stdio.h> -#include <signal.h> -#include <sys/stat.h> -#include <unistd.h> -#include <fcntl.h> - - -#include "shell.h" -#include "main.h" -#include "options.h" -#include "output.h" -#include "parser.h" -#include "nodes.h" -#include "expand.h" -#include "eval.h" -#include "jobs.h" -#include "input.h" -#include "trap.h" -#include "var.h" -#include "show.h" -#include "memalloc.h" -#include "error.h" -#include "init.h" -#include "mystring.h" -#include "exec.h" -#include "cd.h" - -#define PROFILE 0 - -int rootpid; -int rootshell; -STATIC union node *curcmd; -STATIC union node *prevcmd; -#if PROFILE -short profile_buf[16384]; -extern int etext(); -#endif - -STATIC void read_profile(const char *); -STATIC char *find_dot_file(char *); -int main(int, char **); - -/* - * Main routine. We initialize things, parse the arguments, execute - * profiles if we're a login shell, and then call cmdloop to execute - * commands. The setjmp call sets up the location to jump to when an - * exception occurs. When an exception occurs the variable "state" - * is used to figure out how far we had gotten. - */ - -int -main(int argc, char **argv) -{ - struct jmploc jmploc; - struct stackmark smark; - volatile int state; - char *shinit; - -#if PROFILE - monitor(4, etext, profile_buf, sizeof profile_buf, 50); -#endif - state = 0; - if (setjmp(jmploc.loc)) { - /* - * When a shell procedure is executed, we raise the - * exception EXSHELLPROC to clean up before executing - * the shell procedure. - */ - switch (exception) { - case EXSHELLPROC: - rootpid = getpid(); - rootshell = 1; - minusc = NULL; - state = 3; - break; - - case EXEXEC: - exitstatus = exerrno; - break; - - case EXERROR: - exitstatus = 2; - break; - - default: - break; - } - - if (exception != EXSHELLPROC) { - if (state == 0 || iflag == 0 || ! rootshell) - exitshell(exitstatus); - } - reset(); - if (exception == EXINT -#if ATTY - && (! attyset() || equal(termval(), "emacs")) -#endif - ) { - out2c('\n'); - flushout(&errout); - } - popstackmark(&smark); - FORCEINTON; /* enable interrupts */ - if (state == 1) - goto state1; - else if (state == 2) - goto state2; - else if (state == 3) - goto state3; - else - goto state4; - } - handler = &jmploc; -#ifdef DEBUG -#if DEBUG == 2 - debug = 1; -#endif - opentrace(); - trputs("Shell args: "); trargs(argv); -#endif - rootpid = getpid(); - rootshell = 1; - init(); - setstackmark(&smark); - procargs(argc, argv); - if (argv[0] && argv[0][0] == '-') { - state = 1; - read_profile("/etc/profile"); -state1: - state = 2; - read_profile(".profile"); - } -state2: - state = 3; - if (getuid() == geteuid() && getgid() == getegid()) { - if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') { - state = 3; - read_profile(shinit); - } - } -state3: - state = 4; - if (sflag == 0 || minusc) { - static int sigs[] = { - SIGINT, SIGQUIT, SIGHUP, -#ifdef SIGTSTP - SIGTSTP, -#endif - SIGPIPE - }; -#define SIGSSIZE (sizeof(sigs)/sizeof(sigs[0])) - int i; - - for (i = 0; i < SIGSSIZE; i++) - setsignal(sigs[i], 0); - } - - if (minusc) - evalstring(minusc, 0); - - if (sflag || minusc == NULL) { -state4: /* XXX ??? - why isn't this before the "if" statement */ - cmdloop(1); - } -#if PROFILE - monitor(0); -#endif - exitshell(exitstatus); - /* NOTREACHED */ -} - - -/* - * Read and execute commands. "Top" is nonzero for the top level command - * loop; it turns on prompting if the shell is interactive. - */ - -void -cmdloop(int top) -{ - union node *n; - struct stackmark smark; - int inter; - int numeof = 0; - - TRACE(("cmdloop(%d) called\n", top)); - setstackmark(&smark); - for (;;) { - if (pendingsigs) - dotrap(); - inter = 0; - if (iflag && top) { - inter = 1; - showjobs(out2, SHOW_CHANGED); - flushout(&errout); - } - n = parsecmd(inter); - /* showtree(n); DEBUG */ - if (n == NEOF) { - if (!top || numeof >= 50) - break; - if (!stoppedjobs()) { - if (!Iflag) - break; - out2str("\nUse \"exit\" to leave shell.\n"); - } - numeof++; - } else if (n != NULL && nflag == 0) { - job_warning = (job_warning == 2) ? 1 : 0; - numeof = 0; - evaltree(n, 0); - } - popstackmark(&smark); - setstackmark(&smark); - if (evalskip == SKIPFILE) { - evalskip = 0; - break; - } - } - popstackmark(&smark); -} - - - -/* - * Read /etc/profile or .profile. Return on error. - */ - -STATIC void -read_profile(const char *name) -{ - int fd; - int xflag_set = 0; - int vflag_set = 0; - - INTOFF; - if ((fd = open(name, O_RDONLY)) >= 0) - setinputfd(fd, 1); - INTON; - if (fd < 0) - return; - /* -q turns off -x and -v just when executing init files */ - if (qflag) { - if (xflag) - xflag = 0, xflag_set = 1; - if (vflag) - vflag = 0, vflag_set = 1; - } - cmdloop(0); - if (qflag) { - if (xflag_set) - xflag = 1; - if (vflag_set) - vflag = 1; - } - popfile(); -} - - - -/* - * Read a file containing shell functions. - */ - -void -readcmdfile(char *name) -{ - int fd; - - INTOFF; - if ((fd = open(name, O_RDONLY)) >= 0) - setinputfd(fd, 1); - else - error("Can't open %s", name); - INTON; - cmdloop(0); - popfile(); -} - - - -/* - * Take commands from a file. To be compatible we should do a path - * search for the file, which is necessary to find sub-commands. - */ - - -STATIC char * -find_dot_file(char *basename) -{ - char *fullname; - const char *path = pathval(); - struct stat statb; - - /* don't try this for absolute or relative paths */ - if (strchr(basename, '/')) - return basename; - - while ((fullname = padvance(&path, basename)) != NULL) { - if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) { - /* - * Don't bother freeing here, since it will - * be freed by the caller. - */ - return fullname; - } - stunalloc(fullname); - } - - /* not found in the PATH */ - error("%s: not found", basename); - /* NOTREACHED */ -} - -int -dotcmd(int argc, char **argv) -{ - exitstatus = 0; - - if (argc >= 2) { /* That's what SVR2 does */ - char *fullname; - struct stackmark smark; - - setstackmark(&smark); - fullname = find_dot_file(argv[1]); - setinputfile(fullname, 1); - commandname = fullname; - cmdloop(0); - popfile(); - popstackmark(&smark); - } - return exitstatus; -} - - -int -exitcmd(int argc, char **argv) -{ - if (stoppedjobs()) - return 0; - if (argc > 1) - exitstatus = number(argv[1]); - exitshell(exitstatus); - /* NOTREACHED */ -} diff --git a/sh/main.h b/sh/main.h deleted file mode 100644 index d198e2d..0000000 --- a/sh/main.h +++ /dev/null @@ -1,43 +0,0 @@ -/* $NetBSD: main.h,v 1.10 2003/08/07 09:05:34 agc 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 - * Kenneth Almquist. - * - * 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. - * - * @(#)main.h 8.2 (Berkeley) 5/4/95 - */ - -extern int rootpid; /* pid of main shell */ -extern int rootshell; /* true if we aren't a child of the main shell */ - -void readcmdfile(char *); -void cmdloop(int); -int dotcmd(int, char **); -int exitcmd(int, char **); diff --git a/sh/memalloc.c b/sh/memalloc.c deleted file mode 100644 index 07c14db..0000000 --- a/sh/memalloc.c +++ /dev/null @@ -1,307 +0,0 @@ -/* $NetBSD: memalloc.c,v 1.28 2003/08/07 09:05:34 agc 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 - * Kenneth Almquist. - * - * 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[] = "@(#)memalloc.c 8.3 (Berkeley) 5/4/95"; -#else -__RCSID("$NetBSD: memalloc.c,v 1.28 2003/08/07 09:05:34 agc Exp $"); -#endif -#endif /* not lint */ - -#include <stdlib.h> -#include <unistd.h> - -#include "shell.h" -#include "output.h" -#include "memalloc.h" -#include "error.h" -#include "machdep.h" -#include "mystring.h" - -/* - * Like malloc, but returns an error when out of space. - */ - -pointer -ckmalloc(int nbytes) -{ - pointer p; - - p = malloc(nbytes); - if (p == NULL) - error("Out of space"); - return p; -} - - -/* - * Same for realloc. - */ - -pointer -ckrealloc(pointer p, int nbytes) -{ - p = realloc(p, nbytes); - if (p == NULL) - error("Out of space"); - return p; -} - - -/* - * Make a copy of a string in safe storage. - */ - -char * -savestr(const char *s) -{ - char *p; - - p = ckmalloc(strlen(s) + 1); - scopy(s, p); - return p; -} - - -/* - * Parse trees for commands are allocated in lifo order, so we use a stack - * to make this more efficient, and also to avoid all sorts of exception - * handling code to handle interrupts in the middle of a parse. - * - * The size 504 was chosen because the Ultrix malloc handles that size - * well. - */ - -#define MINSIZE 504 /* minimum size of a block */ - -struct stack_block { - struct stack_block *prev; - char space[MINSIZE]; -}; - -struct stack_block stackbase; -struct stack_block *stackp = &stackbase; -struct stackmark *markp; -char *stacknxt = stackbase.space; -int stacknleft = MINSIZE; -int sstrnleft; -int herefd = -1; - -pointer -stalloc(int nbytes) -{ - char *p; - - nbytes = SHELL_ALIGN(nbytes); - if (nbytes > stacknleft) { - int blocksize; - struct stack_block *sp; - - blocksize = nbytes; - if (blocksize < MINSIZE) - blocksize = MINSIZE; - INTOFF; - sp = ckmalloc(sizeof(struct stack_block) - MINSIZE + blocksize); - sp->prev = stackp; - stacknxt = sp->space; - stacknleft = blocksize; - stackp = sp; - INTON; - } - p = stacknxt; - stacknxt += nbytes; - stacknleft -= nbytes; - return p; -} - - -void -stunalloc(pointer p) -{ - if (p == NULL) { /*DEBUG */ - write(2, "stunalloc\n", 10); - abort(); - } - stacknleft += stacknxt - (char *)p; - stacknxt = p; -} - - - -void -setstackmark(struct stackmark *mark) -{ - mark->stackp = stackp; - mark->stacknxt = stacknxt; - mark->stacknleft = stacknleft; - mark->marknext = markp; - markp = mark; -} - - -void -popstackmark(struct stackmark *mark) -{ - struct stack_block *sp; - - INTOFF; - markp = mark->marknext; - while (stackp != mark->stackp) { - sp = stackp; - stackp = sp->prev; - ckfree(sp); - } - stacknxt = mark->stacknxt; - stacknleft = mark->stacknleft; - INTON; -} - - -/* - * When the parser reads in a string, it wants to stick the string on the - * stack and only adjust the stack pointer when it knows how big the - * string is. Stackblock (defined in stack.h) returns a pointer to a block - * of space on top of the stack and stackblocklen returns the length of - * this block. Growstackblock will grow this space by at least one byte, - * possibly moving it (like realloc). Grabstackblock actually allocates the - * part of the block that has been used. - */ - -void -growstackblock(void) -{ - int newlen = SHELL_ALIGN(stacknleft * 2 + 100); - - if (stacknxt == stackp->space && stackp != &stackbase) { - struct stack_block *oldstackp; - struct stackmark *xmark; - struct stack_block *sp; - - INTOFF; - oldstackp = stackp; - sp = stackp; - stackp = sp->prev; - sp = ckrealloc((pointer)sp, - sizeof(struct stack_block) - MINSIZE + newlen); - sp->prev = stackp; - stackp = sp; - stacknxt = sp->space; - stacknleft = newlen; - - /* - * Stack marks pointing to the start of the old block - * must be relocated to point to the new block - */ - xmark = markp; - while (xmark != NULL && xmark->stackp == oldstackp) { - xmark->stackp = stackp; - xmark->stacknxt = stacknxt; - xmark->stacknleft = stacknleft; - xmark = xmark->marknext; - } - INTON; - } else { - char *oldspace = stacknxt; - int oldlen = stacknleft; - char *p = stalloc(newlen); - - (void)memcpy(p, oldspace, oldlen); - stacknxt = p; /* free the space */ - stacknleft += newlen; /* we just allocated */ - } -} - -void -grabstackblock(int len) -{ - len = SHELL_ALIGN(len); - stacknxt += len; - stacknleft -= len; -} - -/* - * The following routines are somewhat easier to use than the above. - * The user declares a variable of type STACKSTR, which may be declared - * to be a register. The macro STARTSTACKSTR initializes things. Then - * the user uses the macro STPUTC to add characters to the string. In - * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is - * grown as necessary. When the user is done, she can just leave the - * string there and refer to it using stackblock(). Or she can allocate - * the space for it using grabstackstr(). If it is necessary to allow - * someone else to use the stack temporarily and then continue to grow - * the string, the user should use grabstack to allocate the space, and - * then call ungrabstr(p) to return to the previous mode of operation. - * - * USTPUTC is like STPUTC except that it doesn't check for overflow. - * CHECKSTACKSPACE can be called before USTPUTC to ensure that there - * is space for at least one character. - */ - -char * -growstackstr(void) -{ - int len = stackblocksize(); - if (herefd >= 0 && len >= 1024) { - xwrite(herefd, stackblock(), len); - sstrnleft = len - 1; - return stackblock(); - } - growstackblock(); - sstrnleft = stackblocksize() - len - 1; - return stackblock() + len; -} - -/* - * Called from CHECKSTRSPACE. - */ - -char * -makestrspace(void) -{ - int len = stackblocksize() - sstrnleft; - growstackblock(); - sstrnleft = stackblocksize() - len; - return stackblock() + len; -} - -void -ungrabstackstr(char *s, char *p) -{ - stacknleft += stacknxt - s; - stacknxt = s; - sstrnleft = stacknleft - (p - s); - -} diff --git a/sh/memalloc.h b/sh/memalloc.h deleted file mode 100644 index e793880..0000000 --- a/sh/memalloc.h +++ /dev/null @@ -1,77 +0,0 @@ -/* $NetBSD: memalloc.h,v 1.14 2003/08/07 09:05:34 agc 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 - * Kenneth Almquist. - * - * 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. - * - * @(#)memalloc.h 8.2 (Berkeley) 5/4/95 - */ - -struct stackmark { - struct stack_block *stackp; - char *stacknxt; - int stacknleft; - struct stackmark *marknext; -}; - - -extern char *stacknxt; -extern int stacknleft; -extern int sstrnleft; -extern int herefd; - -pointer ckmalloc(int); -pointer ckrealloc(pointer, int); -char *savestr(const char *); -pointer stalloc(int); -void stunalloc(pointer); -void setstackmark(struct stackmark *); -void popstackmark(struct stackmark *); -void growstackblock(void); -void grabstackblock(int); -char *growstackstr(void); -char *makestrspace(void); -void ungrabstackstr(char *, char *); - - - -#define stackblock() stacknxt -#define stackblocksize() stacknleft -#define STARTSTACKSTR(p) p = stackblock(), sstrnleft = stackblocksize() -#define STPUTC(c, p) (--sstrnleft >= 0? (*p++ = (c)) : (p = growstackstr(), *p++ = (c))) -#define CHECKSTRSPACE(n, p) { if (sstrnleft < n) p = makestrspace(); } -#define USTPUTC(c, p) (--sstrnleft, *p++ = (c)) -#define STACKSTRNUL(p) (sstrnleft == 0? (p = growstackstr(), *p = '\0') : (*p = '\0')) -#define STUNPUTC(p) (++sstrnleft, --p) -#define STTOPC(p) p[-1] -#define STADJUST(amount, p) (p += (amount), sstrnleft -= (amount)) -#define grabstackstr(p) stalloc(stackblocksize() - sstrnleft) - -#define ckfree(p) free((pointer)(p)) diff --git a/sh/miscbltin.c b/sh/miscbltin.c deleted file mode 100644 index d89029a..0000000 --- a/sh/miscbltin.c +++ /dev/null @@ -1,445 +0,0 @@ -/* $NetBSD: miscbltin.c,v 1.34.2.1 2005/04/07 11:34:20 tron 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 - * Kenneth Almquist. - * - * 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[] = "@(#)miscbltin.c 8.4 (Berkeley) 5/4/95"; -#else -__RCSID("$NetBSD: miscbltin.c,v 1.34.2.1 2005/04/07 11:34:20 tron Exp $"); -#endif -#endif /* not lint */ - -/* - * Miscelaneous builtins. - */ - -#include <sys/types.h> /* quad_t */ -#include <sys/param.h> /* BSD4_4 */ -#include <sys/stat.h> -#include <sys/time.h> -#include <sys/resource.h> -#include <unistd.h> -#include <stdlib.h> -#include <ctype.h> -#include <errno.h> - -#include "shell.h" -#include "options.h" -#include "var.h" -#include "output.h" -#include "memalloc.h" -#include "error.h" -#include "miscbltin.h" -#include "mystring.h" - -#undef rflag - - - -/* - * The read builtin. - * Backslahes escape the next char unless -r is specified. - * - * This uses unbuffered input, which may be avoidable in some cases. - * - * Note that if IFS=' :' then read x y should work so that: - * 'a b' x='a', y='b' - * ' a b ' x='a', y='b' - * ':b' x='', y='b' - * ':' x='', y='' - * '::' x='', y='' - * ': :' x='', y='' - * ':::' x='', y='::' - * ':b c:' x='', y='b c:' - */ - -int -readcmd(int argc, char **argv) -{ - char **ap; - char c; - int rflag; - char *prompt; - const char *ifs; - char *p; - int startword; - int status; - int i; - int is_ifs; - int saveall = 0; - - rflag = 0; - prompt = NULL; - while ((i = nextopt("p:r")) != '\0') { - if (i == 'p') - prompt = optionarg; - else - rflag = 1; - } - - if (prompt && isatty(0)) { - out2str(prompt); - flushall(); - } - - if (*(ap = argptr) == NULL) - error("arg count"); - - if ((ifs = bltinlookup("IFS", 1)) == NULL) - ifs = " \t\n"; - - status = 0; - startword = 2; - STARTSTACKSTR(p); - for (;;) { - if (read(0, &c, 1) != 1) { - status = 1; - break; - } - if (c == '\0') - continue; - if (c == '\\' && !rflag) { - if (read(0, &c, 1) != 1) { - status = 1; - break; - } - if (c != '\n') - STPUTC(c, p); - continue; - } - if (c == '\n') - break; - if (strchr(ifs, c)) - is_ifs = strchr(" \t\n", c) ? 1 : 2; - else - is_ifs = 0; - - if (startword != 0) { - if (is_ifs == 1) { - /* Ignore leading IFS whitespace */ - if (saveall) - STPUTC(c, p); - continue; - } - if (is_ifs == 2 && startword == 1) { - /* Only one non-whitespace IFS per word */ - startword = 2; - if (saveall) - STPUTC(c, p); - continue; - } - } - - if (is_ifs == 0) { - /* append this character to the current variable */ - startword = 0; - if (saveall) - /* Not just a spare terminator */ - saveall++; - STPUTC(c, p); - continue; - } - - /* end of variable... */ - startword = is_ifs; - - if (ap[1] == NULL) { - /* Last variable needs all IFS chars */ - saveall++; - STPUTC(c, p); - continue; - } - - STACKSTRNUL(p); - setvar(*ap, stackblock(), 0); - ap++; - STARTSTACKSTR(p); - } - STACKSTRNUL(p); - - /* Remove trailing IFS chars */ - for (; stackblock() <= --p; *p = 0) { - if (!strchr(ifs, *p)) - break; - if (strchr(" \t\n", *p)) - /* Always remove whitespace */ - continue; - if (saveall > 1) - /* Don't remove non-whitespace unless it was naked */ - break; - } - setvar(*ap, stackblock(), 0); - - /* Set any remaining args to "" */ - while (*++ap != NULL) - setvar(*ap, nullstr, 0); - return status; -} - - - -int -umaskcmd(int argc, char **argv) -{ - char *ap; - int mask; - int i; - int symbolic_mode = 0; - - while ((i = nextopt("S")) != '\0') { - symbolic_mode = 1; - } - - INTOFF; - mask = umask(0); - umask(mask); - INTON; - - if ((ap = *argptr) == NULL) { - if (symbolic_mode) { - char u[4], g[4], o[4]; - - i = 0; - if ((mask & S_IRUSR) == 0) - u[i++] = 'r'; - if ((mask & S_IWUSR) == 0) - u[i++] = 'w'; - if ((mask & S_IXUSR) == 0) - u[i++] = 'x'; - u[i] = '\0'; - - i = 0; - if ((mask & S_IRGRP) == 0) - g[i++] = 'r'; - if ((mask & S_IWGRP) == 0) - g[i++] = 'w'; - if ((mask & S_IXGRP) == 0) - g[i++] = 'x'; - g[i] = '\0'; - - i = 0; - if ((mask & S_IROTH) == 0) - o[i++] = 'r'; - if ((mask & S_IWOTH) == 0) - o[i++] = 'w'; - if ((mask & S_IXOTH) == 0) - o[i++] = 'x'; - o[i] = '\0'; - - out1fmt("u=%s,g=%s,o=%s\n", u, g, o); - } else { - out1fmt("%.4o\n", mask); - } - } else { - if (isdigit((unsigned char)*ap)) { - mask = 0; - do { - if (*ap >= '8' || *ap < '0') - error("Illegal number: %s", argv[1]); - mask = (mask << 3) + (*ap - '0'); - } while (*++ap != '\0'); - umask(mask); - } else - error("Illegal mode: %s", ap); - } - return 0; -} - -#if 1 -/* - * ulimit builtin - * - * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and - * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with - * ash by J.T. Conklin. - * - * Public domain. - */ - -struct limits { - const char *name; - int cmd; - int factor; /* multiply by to get rlim_{cur,max} values */ - char option; -}; - -static const struct limits limits[] = { -#ifdef RLIMIT_CPU - { "time(seconds)", RLIMIT_CPU, 1, 't' }, -#endif -#ifdef RLIMIT_FSIZE - { "file(blocks)", RLIMIT_FSIZE, 512, 'f' }, -#endif -#ifdef RLIMIT_DATA - { "data(kbytes)", RLIMIT_DATA, 1024, 'd' }, -#endif -#ifdef RLIMIT_STACK - { "stack(kbytes)", RLIMIT_STACK, 1024, 's' }, -#endif -#ifdef RLIMIT_CORE - { "coredump(blocks)", RLIMIT_CORE, 512, 'c' }, -#endif -#ifdef RLIMIT_RSS - { "memory(kbytes)", RLIMIT_RSS, 1024, 'm' }, -#endif -#ifdef RLIMIT_MEMLOCK - { "locked memory(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' }, -#endif -#ifdef RLIMIT_NPROC - { "process(processes)", RLIMIT_NPROC, 1, 'p' }, -#endif -#ifdef RLIMIT_NOFILE - { "nofiles(descriptors)", RLIMIT_NOFILE, 1, 'n' }, -#endif -#ifdef RLIMIT_VMEM - { "vmemory(kbytes)", RLIMIT_VMEM, 1024, 'v' }, -#endif -#ifdef RLIMIT_SWAP - { "swap(kbytes)", RLIMIT_SWAP, 1024, 'w' }, -#endif -#ifdef RLIMIT_SBSIZE - { "sbsize(bytes)", RLIMIT_SBSIZE, 1, 'b' }, -#endif - { (char *) 0, 0, 0, '\0' } -}; - -int -ulimitcmd(int argc, char **argv) -{ - int c; - rlim_t val = 0; - enum { SOFT = 0x1, HARD = 0x2 } - how = SOFT | HARD; - const struct limits *l; - int set, all = 0; - int optc, what; - struct rlimit limit; - - what = 'f'; - while ((optc = nextopt("HSabtfdsmcnpl")) != '\0') - switch (optc) { - case 'H': - how = HARD; - break; - case 'S': - how = SOFT; - break; - case 'a': - all = 1; - break; - default: - what = optc; - } - - for (l = limits; l->name && l->option != what; l++) - ; - if (!l->name) - error("internal error (%c)", what); - - set = *argptr ? 1 : 0; - if (set) { - char *p = *argptr; - - if (all || argptr[1]) - error("too many arguments"); - if (strcmp(p, "unlimited") == 0) - val = RLIM_INFINITY; - else { - val = (rlim_t) 0; - - while ((c = *p++) >= '0' && c <= '9') - { - val = (val * 10) + (long)(c - '0'); - if ((long)val < 0) - break; - } - if (c) - error("bad number"); - val *= l->factor; - } - } - if (all) { - for (l = limits; l->name; l++) { - getrlimit(l->cmd, &limit); - if (how & SOFT) - val = limit.rlim_cur; - else if (how & HARD) - val = limit.rlim_max; - - out1fmt("%-20s ", l->name); - if (val == RLIM_INFINITY) - out1fmt("unlimited\n"); - else - { - val /= l->factor; -#ifdef BSD4_4 - out1fmt("%lld\n", (long long) val); -#else - out1fmt("%ld\n", (long) val); -#endif - } - } - return 0; - } - - getrlimit(l->cmd, &limit); - if (set) { - if (how & HARD) - limit.rlim_max = val; - if (how & SOFT) - limit.rlim_cur = val; - if (setrlimit(l->cmd, &limit) < 0) - error("error setting limit (%s)", strerror(errno)); - } else { - if (how & SOFT) - val = limit.rlim_cur; - else if (how & HARD) - val = limit.rlim_max; - - if (val == RLIM_INFINITY) - out1fmt("unlimited\n"); - else - { - val /= l->factor; -#ifdef BSD4_4 - out1fmt("%lld\n", (long long) val); -#else - out1fmt("%ld\n", (long) val); -#endif - } - } - return 0; -} -#endif diff --git a/sh/miscbltin.h b/sh/miscbltin.h deleted file mode 100644 index 4c12c82..0000000 --- a/sh/miscbltin.h +++ /dev/null @@ -1,31 +0,0 @@ -/* $NetBSD: miscbltin.h,v 1.3 2003/08/21 17:57:53 christos Exp $ */ - -/* - * Copyright (c) 1997 Christos Zoulas. 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. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. - */ - -int readcmd(int, char **); -int umaskcmd(int, char **); -int ulimitcmd(int, char **); diff --git a/sh/mkbuiltins b/sh/mkbuiltins deleted file mode 100644 index 5b19269..0000000 --- a/sh/mkbuiltins +++ /dev/null @@ -1,136 +0,0 @@ -#!/bin/sh - -# $NetBSD: mkbuiltins,v 1.21 2004/06/06 07:03:11 christos 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 -# Kenneth Almquist. -# -# 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. -# -# @(#)mkbuiltins 8.2 (Berkeley) 5/4/95 - -havehist=1 -if [ "X$1" = "X-h" ]; then - havehist=0 - shift -fi - -shell=$1 -builtins=$2 -objdir=$3 - -havejobs=0 -if grep '^#define JOBS[ ]*1' ${shell} > /dev/null -then - havejobs=1 -fi - -exec <$builtins 3> ${objdir}/builtins.c 4> ${objdir}/builtins.h - -echo '/* - * This file was generated by the mkbuiltins program. - */ - -#include "shell.h" -#include "builtins.h" - -const struct builtincmd builtincmd[] = { -' >&3 - -echo '/* - * This file was generated by the mkbuiltins program. - */ - -#include <sys/cdefs.h> - -struct builtincmd { - const char *name; - int (*builtin)(int, char **); -}; - -extern const struct builtincmd builtincmd[]; -extern const struct builtincmd splbltincmd[]; - -' >&4 - -specials= - -while read line -do - set -- $line - [ -z "$1" ] && continue - case "$1" in - \#if*|\#def*|\#end*) - echo $line >&3 - echo $line >&4 - continue - ;; - esac - l1="${line###}" - [ "$l1" != "$line" ] && continue - - - func=$1 - shift - [ x"$1" = x'-j' ] && { - [ $havejobs = 0 ] && continue - shift - } - [ x"$1" = x'-h' ] && { - [ $havehist = 0 ] && continue - shift - } - echo 'int '"$func"'(int, char **);' >&4 - while - [ $# != 0 -a "$1" != '#' ] - do - [ "$1" = '-s' ] && { - specials="$specials $2 $func" - shift 2 - continue; - } - [ "$1" = '-u' ] && shift - echo ' { "'$1'", '"$func"' },' >&3 - shift - done -done - -echo ' { 0, 0 },' >&3 -echo '};' >&3 -echo >&3 -echo 'const struct builtincmd splbltincmd[] = {' >&3 - -set -- $specials -while - [ $# != 0 ] -do - echo ' { "'$1'", '"$2"' },' >&3 - shift 2 -done - -echo ' { 0, 0 },' >&3 -echo "};" >&3 diff --git a/sh/mkinit.sh b/sh/mkinit.sh deleted file mode 100644 index cae27dd..0000000 --- a/sh/mkinit.sh +++ /dev/null @@ -1,197 +0,0 @@ -#! /bin/sh -# $NetBSD: mkinit.sh,v 1.2 2004/06/15 23:09:54 dsl Exp $ - -# Copyright (c) 2003 The NetBSD Foundation, Inc. -# All rights reserved. -# -# This code is derived from software contributed to The NetBSD Foundation -# by David Laight. -# -# 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 NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. - -srcs="$*" - -nl=' -' -openparen='(' -backslash='\' - -includes=' "shell.h" "mystring.h" "init.h" ' -defines= -decles= -event_init= -event_reset= -event_shellproc= - -for src in $srcs; do - exec <$src - decnl="$nl" - while IFS=; read -r line; do - [ "$line" = x ] - case "$line " in - INIT["{ "]* ) event=init;; - RESET["{ "]* ) event=reset;; - SHELLPROC["{ "]* ) event=shellproc;; - INCLUDE[\ \ ]* ) - IFS=' ' - set -- $line - # ignore duplicates - [ "${includes}" != "${includes%* $2 }" ] && continue - includes="$includes$2 " - continue - ;; - MKINIT\ ) - # struct declaration - decles="$decles$nl" - while - read -r line - decles="${decles}${line}${nl}" - [ "$line" != "};" ] - do - : - done - decnl="$nl" - continue - ;; - MKINIT["{ "]* ) - # strip initialiser - def=${line#MKINIT} - comment="${def#*;}" - def="${def%;$comment}" - def="${def%%=*}" - def="${def% }" - decles="${decles}${decnl}extern${def};${comment}${nl}" - decnl= - continue - ;; - \#define[\ \ ]* ) - IFS=' ' - set -- $line - # Ignore those with arguments - [ "$2" = "${2##*$openparen}" ] || continue - # and multiline definitions - [ "$line" = "${line%$backslash}" ] || continue - defines="${defines}#undef $2${nl}${line}${nl}" - continue - ;; - * ) continue;; - esac - # code for events - ev="${nl} /* from $src: */${nl} {${nl}" - while - read -r line - [ "$line" != "}" ] - do - # The C program indented by an extra 6 chars using - # tabs then spaces. I need to compare the output :-( - indent=6 - while - l=${line# } - [ "$l" != "$line" ] - do - indent=$(($indent + 8)) - line="$l" - done - while - l=${line# } - [ "$l" != "$line" ] - do - indent=$(($indent + 1)) - line="$l" - done - [ -z "$line" -o "$line" != "${line###}" ] && indent=0 - while - [ $indent -ge 8 ] - do - ev="$ev " - indent="$(($indent - 8))" - done - while - [ $indent -gt 0 ] - do - ev="$ev " - indent="$(($indent - 1))" - done - ev="${ev}${line}${nl}" - done - ev="${ev} }${nl}" - eval event_$event=\"\$event_$event\$ev\" - done -done - -exec >init.c.tmp - -echo "/*" -echo " * This file was generated by the mkinit program." -echo " */" -echo - -IFS=' ' -for f in $includes; do - echo "#include $f" -done - -echo -echo -echo -echo "$defines" -echo -echo "$decles" -echo -echo -echo "/*" -echo " * Initialization code." -echo " */" -echo -echo "void" -echo "init() {" -echo "${event_init%$nl}" -echo "}" -echo -echo -echo -echo "/*" -echo " * This routine is called when an error or an interrupt occurs in an" -echo " * interactive shell and control is returned to the main command loop." -echo " */" -echo -echo "void" -echo "reset() {" -echo "${event_reset%$nl}" -echo "}" -echo -echo -echo -echo "/*" -echo " * This routine is called to initialize the shell to run a shell procedure." -echo " */" -echo -echo "void" -echo "initshellproc() {" -echo "${event_shellproc%$nl}" -echo "}" - -exec >&- -mv init.c.tmp init.c diff --git a/sh/mknodes.sh b/sh/mknodes.sh deleted file mode 100644 index 54d2e3d..0000000 --- a/sh/mknodes.sh +++ /dev/null @@ -1,217 +0,0 @@ -#! /bin/sh -# $NetBSD: mknodes.sh,v 1.1 2004/01/16 23:24:38 dsl Exp $ - -# Copyright (c) 2003 The NetBSD Foundation, Inc. -# All rights reserved. -# -# This code is derived from software contributed to The NetBSD Foundation -# by David Laight. -# -# 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 NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. - -nodetypes=$1 -nodes_pat=$2 -objdir="$3" - -exec <$nodetypes -exec >$objdir/nodes.h.tmp - -echo "/*" -echo " * This file was generated by mknodes.sh" -echo " */" -echo - -tagno=0 -while IFS=; read -r line; do - line="${line%%#*}" - IFS=' ' - set -- $line - IFS= - [ -z "$2" ] && continue - case "$line" in - [" "]* ) - IFS=' ' - [ $field = 0 ] && struct_list="$struct_list $struct" - eval field_${struct}_$field=\"\$*\" - eval numfld_$struct=\$field - field=$(($field + 1)) - ;; - * ) - define=$1 - struct=$2 - echo "#define $define $tagno" - tagno=$(($tagno + 1)) - eval define_$struct=\"\$define_$struct \$define\" - struct_define="$struct_define $struct" - field=0 - ;; - esac -done - -echo - -IFS=' ' -for struct in $struct_list; do - echo - echo - echo "struct $struct {" - field=0 - while - eval line=\"\$field_${struct}_$field\" - field=$(($field + 1)) - [ -n "$line" ] - do - IFS=' ' - set -- $line - name=$1 - case $2 in - nodeptr ) type="union node *";; - nodelist ) type="struct nodelist *";; - string ) type="char *";; - int ) type="int ";; - * ) name=; shift 2; type="$*";; - esac - echo " $type$name;" - done - echo "};" -done - -echo -echo -echo "union node {" -echo " int type;" -for struct in $struct_list; do - echo " struct $struct $struct;" -done -echo "};" -echo -echo -echo "struct nodelist {" -echo " struct nodelist *next;" -echo " union node *n;" -echo "};" -echo -echo -echo "union node *copyfunc(union node *);" -echo "void freefunc(union node *);" - -mv $objdir/nodes.h.tmp $objdir/nodes.h || exit 1 - -exec <$nodes_pat -exec >$objdir/nodes.c.tmp - -echo "/*" -echo " * This file was generated by mknodes.sh" -echo " */" -echo - -while IFS=; read -r line; do - IFS=' ' - set -- $line - IFS= - case "$1" in - '%SIZES' ) - echo "static const short nodesize[$tagno] = {" - IFS=' ' - for struct in $struct_define; do - echo " SHELL_ALIGN(sizeof (struct $struct))," - done - echo "};" - ;; - '%CALCSIZE' ) - echo " if (n == NULL)" - echo " return;" - echo " funcblocksize += nodesize[n->type];" - echo " switch (n->type) {" - IFS=' ' - for struct in $struct_list; do - eval defines=\"\$define_$struct\" - for define in $defines; do - echo " case $define:" - done - eval field=\$numfld_$struct - while - [ $field != 0 ] - do - eval line=\"\$field_${struct}_$field\" - field=$(($field - 1)) - IFS=' ' - set -- $line - name=$1 - cl=")" - case $2 in - nodeptr ) fn=calcsize;; - nodelist ) fn=sizenodelist;; - string ) fn="funcstringsize += strlen" - cl=") + 1";; - * ) continue;; - esac - echo " ${fn}(n->$struct.$name${cl};" - done - echo " break;" - done - echo " };" - ;; - '%COPY' ) - echo " if (n == NULL)" - echo " return NULL;" - echo " new = funcblock;" - echo " funcblock = (char *) funcblock + nodesize[n->type];" - echo " switch (n->type) {" - IFS=' ' - for struct in $struct_list; do - eval defines=\"\$define_$struct\" - for define in $defines; do - echo " case $define:" - done - eval field=\$numfld_$struct - while - [ $field != 0 ] - do - eval line=\"\$field_${struct}_$field\" - field=$(($field - 1)) - IFS=' ' - set -- $line - name=$1 - case $2 in - nodeptr ) fn="copynode(";; - nodelist ) fn="copynodelist(";; - string ) fn="nodesavestr(";; - int ) fn=;; - * ) continue;; - esac - f="$struct.$name" - echo " new->$f = ${fn}n->$f${fn:+)};" - done - echo " break;" - done - echo " };" - echo " new->type = n->type;" - ;; - * ) echo "$line";; - esac -done - -mv $objdir/nodes.c.tmp $objdir/nodes.c || exit 1 diff --git a/sh/mktokens b/sh/mktokens deleted file mode 100644 index 25f2e6e..0000000 --- a/sh/mktokens +++ /dev/null @@ -1,92 +0,0 @@ -#!/bin/sh - -# $NetBSD: mktokens,v 1.10 2003/08/22 11:22:23 agc 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 -# Kenneth Almquist. -# -# 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. -# -# @(#)mktokens 8.1 (Berkeley) 5/31/93 - -# The following is a list of tokens. The second column is nonzero if the -# token marks the end of a list. The third column is the name to print in -# error messages. - -cat > /tmp/ka$$ <<\! -TEOF 1 end of file -TNL 0 newline -TSEMI 0 ";" -TBACKGND 0 "&" -TAND 0 "&&" -TOR 0 "||" -TPIPE 0 "|" -TLP 0 "(" -TRP 1 ")" -TENDCASE 1 ";;" -TENDBQUOTE 1 "`" -TREDIR 0 redirection -TWORD 0 word -TIF 0 "if" -TTHEN 1 "then" -TELSE 1 "else" -TELIF 1 "elif" -TFI 1 "fi" -TWHILE 0 "while" -TUNTIL 0 "until" -TFOR 0 "for" -TDO 1 "do" -TDONE 1 "done" -TBEGIN 0 "{" -TEND 1 "}" -TCASE 0 "case" -TESAC 1 "esac" -TNOT 0 "!" -! -nl=`wc -l /tmp/ka$$` -exec > token.h -awk '{print "#define " $1 " " NR-1}' /tmp/ka$$ -echo ' -/* Array indicating which tokens mark the end of a list */ -const char tokendlist[] = {' -awk '{print "\t" $2 ","}' /tmp/ka$$ -echo '}; - -const char *const tokname[] = {' -sed -e 's/"/\\"/g' \ - -e 's/[^ ]*[ ][ ]*[^ ]*[ ][ ]*\(.*\)/ "\1",/' \ - /tmp/ka$$ -echo '}; -' -sed 's/"//g' /tmp/ka$$ | awk ' -/TIF/{print "#define KWDOFFSET " NR-1; print ""; - print "const char *const parsekwd[] = {"} -/TIF/,/neverfound/{print " \"" $3 "\","}' -echo ' 0 -};' - -rm /tmp/ka$$ diff --git a/sh/myhistedit.h b/sh/myhistedit.h deleted file mode 100644 index 603a27b..0000000 --- a/sh/myhistedit.h +++ /dev/null @@ -1,49 +0,0 @@ -/* $NetBSD: myhistedit.h,v 1.10 2003/08/07 09:05:35 agc Exp $ */ - -/*- - * Copyright (c) 1993 - * 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. - * - * @(#)myhistedit.h 8.2 (Berkeley) 5/4/95 - */ - -#ifdef WITH_HISTORY -#include <histedit.h> - -extern History *hist; -extern EditLine *el; -extern int displayhist; - -void histedit(void); -void sethistsize(const char *); -void setterm(const char *); -int histcmd(int, char **); -int inputrc(int, char **); -int not_fcnumber(char *); -int str_to_event(const char *, int); -#endif - diff --git a/sh/mystring.c b/sh/mystring.c deleted file mode 100644 index aecf83e..0000000 --- a/sh/mystring.c +++ /dev/null @@ -1,133 +0,0 @@ -/* $NetBSD: mystring.c,v 1.16 2003/08/07 09:05:35 agc 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 - * Kenneth Almquist. - * - * 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[] = "@(#)mystring.c 8.2 (Berkeley) 5/4/95"; -#else -__RCSID("$NetBSD: mystring.c,v 1.16 2003/08/07 09:05:35 agc Exp $"); -#endif -#endif /* not lint */ - -/* - * String functions. - * - * equal(s1, s2) Return true if strings are equal. - * scopy(from, to) Copy a string. - * scopyn(from, to, n) Like scopy, but checks for overflow. - * number(s) Convert a string of digits to an integer. - * is_number(s) Return true if s is a string of digits. - */ - -#include <stdlib.h> -#include "shell.h" -#include "syntax.h" -#include "error.h" -#include "mystring.h" - - -char nullstr[1]; /* zero length string */ - -/* - * equal - #defined in mystring.h - */ - -/* - * scopy - #defined in mystring.h - */ - - -/* - * scopyn - copy a string from "from" to "to", truncating the string - * if necessary. "To" is always nul terminated, even if - * truncation is performed. "Size" is the size of "to". - */ - -void -scopyn(const char *from, char *to, int size) -{ - - while (--size > 0) { - if ((*to++ = *from++) == '\0') - return; - } - *to = '\0'; -} - - -/* - * prefix -- see if pfx is a prefix of string. - */ - -int -prefix(const char *pfx, const char *string) -{ - while (*pfx) { - if (*pfx++ != *string++) - return 0; - } - return 1; -} - - -/* - * Convert a string of digits to an integer, printing an error message on - * failure. - */ - -int -number(const char *s) -{ - - if (! is_number(s)) - error("Illegal number: %s", s); - return atoi(s); -} - - - -/* - * Check for a valid number. This should be elsewhere. - */ - -int -is_number(const char *p) -{ - do { - if (! is_digit(*p)) - return 0; - } while (*++p != '\0'); - return 1; -} diff --git a/sh/mystring.h b/sh/mystring.h deleted file mode 100644 index 08a73e9..0000000 --- a/sh/mystring.h +++ /dev/null @@ -1,45 +0,0 @@ -/* $NetBSD: mystring.h,v 1.11 2003/08/07 09:05:35 agc 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 - * Kenneth Almquist. - * - * 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. - * - * @(#)mystring.h 8.2 (Berkeley) 5/4/95 - */ - -#include <string.h> - -void scopyn(const char *, char *, int); -int prefix(const char *, const char *); -int number(const char *); -int is_number(const char *); - -#define equal(s1, s2) (strcmp(s1, s2) == 0) -#define scopy(s1, s2) ((void)strcpy(s2, s1)) diff --git a/sh/nodes.c b/sh/nodes.c deleted file mode 100644 index 8a2c718..0000000 --- a/sh/nodes.c +++ /dev/null @@ -1,347 +0,0 @@ -/* - * This file was generated by mknodes.sh - */ - -/* $NetBSD: nodes.c.pat,v 1.12 2004/06/15 22:57:27 dsl 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 - * Kenneth Almquist. - * - * 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. - * - * @(#)nodes.c.pat 8.2 (Berkeley) 5/4/95 - */ - -#include <stdlib.h> -/* - * Routine for dealing with parsed shell commands. - */ - -#include "shell.h" -#include "nodes.h" -#include "memalloc.h" -#include "machdep.h" -#include "mystring.h" - - -int funcblocksize; /* size of structures in function */ -int funcstringsize; /* size of strings in node */ -pointer funcblock; /* block to allocate function from */ -char *funcstring; /* block to allocate strings from */ - -static const short nodesize[26] = { - SHELL_ALIGN(sizeof (struct nbinary)), - SHELL_ALIGN(sizeof (struct ncmd)), - SHELL_ALIGN(sizeof (struct npipe)), - SHELL_ALIGN(sizeof (struct nredir)), - SHELL_ALIGN(sizeof (struct nredir)), - SHELL_ALIGN(sizeof (struct nredir)), - SHELL_ALIGN(sizeof (struct nbinary)), - SHELL_ALIGN(sizeof (struct nbinary)), - SHELL_ALIGN(sizeof (struct nif)), - SHELL_ALIGN(sizeof (struct nbinary)), - SHELL_ALIGN(sizeof (struct nbinary)), - SHELL_ALIGN(sizeof (struct nfor)), - SHELL_ALIGN(sizeof (struct ncase)), - SHELL_ALIGN(sizeof (struct nclist)), - SHELL_ALIGN(sizeof (struct narg)), - SHELL_ALIGN(sizeof (struct narg)), - SHELL_ALIGN(sizeof (struct nfile)), - SHELL_ALIGN(sizeof (struct nfile)), - SHELL_ALIGN(sizeof (struct nfile)), - SHELL_ALIGN(sizeof (struct nfile)), - SHELL_ALIGN(sizeof (struct nfile)), - SHELL_ALIGN(sizeof (struct ndup)), - SHELL_ALIGN(sizeof (struct ndup)), - SHELL_ALIGN(sizeof (struct nhere)), - SHELL_ALIGN(sizeof (struct nhere)), - SHELL_ALIGN(sizeof (struct nnot)), -}; - - -STATIC void calcsize(union node *); -STATIC void sizenodelist(struct nodelist *); -STATIC union node *copynode(union node *); -STATIC struct nodelist *copynodelist(struct nodelist *); -STATIC char *nodesavestr(char *); - - - -/* - * Make a copy of a parse tree. - */ - -union node * -copyfunc(n) - union node *n; -{ - if (n == NULL) - return NULL; - funcblocksize = 0; - funcstringsize = 0; - calcsize(n); - funcblock = ckmalloc(funcblocksize + funcstringsize); - funcstring = (char *) funcblock + funcblocksize; - return copynode(n); -} - - - -STATIC void -calcsize(n) - union node *n; -{ - if (n == NULL) - return; - funcblocksize += nodesize[n->type]; - switch (n->type) { - case NSEMI: - case NAND: - case NOR: - case NWHILE: - case NUNTIL: - calcsize(n->nbinary.ch2); - calcsize(n->nbinary.ch1); - break; - case NCMD: - calcsize(n->ncmd.redirect); - calcsize(n->ncmd.args); - break; - case NPIPE: - sizenodelist(n->npipe.cmdlist); - break; - case NREDIR: - case NBACKGND: - case NSUBSHELL: - calcsize(n->nredir.redirect); - calcsize(n->nredir.n); - break; - case NIF: - calcsize(n->nif.elsepart); - calcsize(n->nif.ifpart); - calcsize(n->nif.test); - break; - case NFOR: - funcstringsize += strlen(n->nfor.var) + 1; - calcsize(n->nfor.body); - calcsize(n->nfor.args); - break; - case NCASE: - calcsize(n->ncase.cases); - calcsize(n->ncase.expr); - break; - case NCLIST: - calcsize(n->nclist.body); - calcsize(n->nclist.pattern); - calcsize(n->nclist.next); - break; - case NDEFUN: - case NARG: - sizenodelist(n->narg.backquote); - funcstringsize += strlen(n->narg.text) + 1; - calcsize(n->narg.next); - break; - case NTO: - case NCLOBBER: - case NFROM: - case NFROMTO: - case NAPPEND: - calcsize(n->nfile.fname); - calcsize(n->nfile.next); - break; - case NTOFD: - case NFROMFD: - calcsize(n->ndup.vname); - calcsize(n->ndup.next); - break; - case NHERE: - case NXHERE: - calcsize(n->nhere.doc); - calcsize(n->nhere.next); - break; - case NNOT: - calcsize(n->nnot.com); - break; - }; -} - - - -STATIC void -sizenodelist(lp) - struct nodelist *lp; -{ - while (lp) { - funcblocksize += SHELL_ALIGN(sizeof(struct nodelist)); - calcsize(lp->n); - lp = lp->next; - } -} - - - -STATIC union node * -copynode(n) - union node *n; -{ - union node *new; - - if (n == NULL) - return NULL; - new = funcblock; - funcblock = (char *) funcblock + nodesize[n->type]; - switch (n->type) { - case NSEMI: - case NAND: - case NOR: - case NWHILE: - case NUNTIL: - new->nbinary.ch2 = copynode(n->nbinary.ch2); - new->nbinary.ch1 = copynode(n->nbinary.ch1); - break; - case NCMD: - new->ncmd.redirect = copynode(n->ncmd.redirect); - new->ncmd.args = copynode(n->ncmd.args); - new->ncmd.backgnd = n->ncmd.backgnd; - break; - case NPIPE: - new->npipe.cmdlist = copynodelist(n->npipe.cmdlist); - new->npipe.backgnd = n->npipe.backgnd; - break; - case NREDIR: - case NBACKGND: - case NSUBSHELL: - new->nredir.redirect = copynode(n->nredir.redirect); - new->nredir.n = copynode(n->nredir.n); - break; - case NIF: - new->nif.elsepart = copynode(n->nif.elsepart); - new->nif.ifpart = copynode(n->nif.ifpart); - new->nif.test = copynode(n->nif.test); - break; - case NFOR: - new->nfor.var = nodesavestr(n->nfor.var); - new->nfor.body = copynode(n->nfor.body); - new->nfor.args = copynode(n->nfor.args); - break; - case NCASE: - new->ncase.cases = copynode(n->ncase.cases); - new->ncase.expr = copynode(n->ncase.expr); - break; - case NCLIST: - new->nclist.body = copynode(n->nclist.body); - new->nclist.pattern = copynode(n->nclist.pattern); - new->nclist.next = copynode(n->nclist.next); - break; - case NDEFUN: - case NARG: - new->narg.backquote = copynodelist(n->narg.backquote); - new->narg.text = nodesavestr(n->narg.text); - new->narg.next = copynode(n->narg.next); - break; - case NTO: - case NCLOBBER: - case NFROM: - case NFROMTO: - case NAPPEND: - new->nfile.fname = copynode(n->nfile.fname); - new->nfile.fd = n->nfile.fd; - new->nfile.next = copynode(n->nfile.next); - break; - case NTOFD: - case NFROMFD: - new->ndup.vname = copynode(n->ndup.vname); - new->ndup.dupfd = n->ndup.dupfd; - new->ndup.fd = n->ndup.fd; - new->ndup.next = copynode(n->ndup.next); - break; - case NHERE: - case NXHERE: - new->nhere.doc = copynode(n->nhere.doc); - new->nhere.fd = n->nhere.fd; - new->nhere.next = copynode(n->nhere.next); - break; - case NNOT: - new->nnot.com = copynode(n->nnot.com); - break; - }; - new->type = n->type; - return new; -} - - -STATIC struct nodelist * -copynodelist(lp) - struct nodelist *lp; -{ - struct nodelist *start; - struct nodelist **lpp; - - lpp = &start; - while (lp) { - *lpp = funcblock; - funcblock = (char *) funcblock + - SHELL_ALIGN(sizeof(struct nodelist)); - (*lpp)->n = copynode(lp->n); - lp = lp->next; - lpp = &(*lpp)->next; - } - *lpp = NULL; - return start; -} - - - -STATIC char * -nodesavestr(s) - char *s; -{ - register char *p = s; - register char *q = funcstring; - char *rtn = funcstring; - - while ((*q++ = *p++) != 0) - continue; - funcstring = q; - return rtn; -} - - - -/* - * Free a parse tree. - */ - -void -freefunc(n) - union node *n; -{ - if (n) - ckfree(n); -} diff --git a/sh/nodes.c.pat b/sh/nodes.c.pat deleted file mode 100644 index e619a01..0000000 --- a/sh/nodes.c.pat +++ /dev/null @@ -1,166 +0,0 @@ -/* $NetBSD: nodes.c.pat,v 1.12 2004/06/15 22:57:27 dsl 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 - * Kenneth Almquist. - * - * 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. - * - * @(#)nodes.c.pat 8.2 (Berkeley) 5/4/95 - */ - -#include <stdlib.h> -/* - * Routine for dealing with parsed shell commands. - */ - -#include "shell.h" -#include "nodes.h" -#include "memalloc.h" -#include "machdep.h" -#include "mystring.h" - - -int funcblocksize; /* size of structures in function */ -int funcstringsize; /* size of strings in node */ -pointer funcblock; /* block to allocate function from */ -char *funcstring; /* block to allocate strings from */ - -%SIZES - - -STATIC void calcsize(union node *); -STATIC void sizenodelist(struct nodelist *); -STATIC union node *copynode(union node *); -STATIC struct nodelist *copynodelist(struct nodelist *); -STATIC char *nodesavestr(char *); - - - -/* - * Make a copy of a parse tree. - */ - -union node * -copyfunc(n) - union node *n; -{ - if (n == NULL) - return NULL; - funcblocksize = 0; - funcstringsize = 0; - calcsize(n); - funcblock = ckmalloc(funcblocksize + funcstringsize); - funcstring = (char *) funcblock + funcblocksize; - return copynode(n); -} - - - -STATIC void -calcsize(n) - union node *n; -{ - %CALCSIZE -} - - - -STATIC void -sizenodelist(lp) - struct nodelist *lp; -{ - while (lp) { - funcblocksize += SHELL_ALIGN(sizeof(struct nodelist)); - calcsize(lp->n); - lp = lp->next; - } -} - - - -STATIC union node * -copynode(n) - union node *n; -{ - union node *new; - - %COPY - return new; -} - - -STATIC struct nodelist * -copynodelist(lp) - struct nodelist *lp; -{ - struct nodelist *start; - struct nodelist **lpp; - - lpp = &start; - while (lp) { - *lpp = funcblock; - funcblock = (char *) funcblock + - SHELL_ALIGN(sizeof(struct nodelist)); - (*lpp)->n = copynode(lp->n); - lp = lp->next; - lpp = &(*lpp)->next; - } - *lpp = NULL; - return start; -} - - - -STATIC char * -nodesavestr(s) - char *s; -{ - register char *p = s; - register char *q = funcstring; - char *rtn = funcstring; - - while ((*q++ = *p++) != 0) - continue; - funcstring = q; - return rtn; -} - - - -/* - * Free a parse tree. - */ - -void -freefunc(n) - union node *n; -{ - if (n) - ckfree(n); -} diff --git a/sh/nodes.h b/sh/nodes.h deleted file mode 100644 index aa750ed..0000000 --- a/sh/nodes.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - * This file was generated by mknodes.sh - */ - -#define NSEMI 0 -#define NCMD 1 -#define NPIPE 2 -#define NREDIR 3 -#define NBACKGND 4 -#define NSUBSHELL 5 -#define NAND 6 -#define NOR 7 -#define NIF 8 -#define NWHILE 9 -#define NUNTIL 10 -#define NFOR 11 -#define NCASE 12 -#define NCLIST 13 -#define NDEFUN 14 -#define NARG 15 -#define NTO 16 -#define NCLOBBER 17 -#define NFROM 18 -#define NFROMTO 19 -#define NAPPEND 20 -#define NTOFD 21 -#define NFROMFD 22 -#define NHERE 23 -#define NXHERE 24 -#define NNOT 25 - - - -struct nbinary { - int type; - union node *ch1; - union node *ch2; -}; - - -struct ncmd { - int type; - int backgnd; - union node *args; - union node *redirect; -}; - - -struct npipe { - int type; - int backgnd; - struct nodelist *cmdlist; -}; - - -struct nredir { - int type; - union node *n; - union node *redirect; -}; - - -struct nif { - int type; - union node *test; - union node *ifpart; - union node *elsepart; -}; - - -struct nfor { - int type; - union node *args; - union node *body; - char *var; -}; - - -struct ncase { - int type; - union node *expr; - union node *cases; -}; - - -struct nclist { - int type; - union node *next; - union node *pattern; - union node *body; -}; - - -struct narg { - int type; - union node *next; - char *text; - struct nodelist *backquote; -}; - - -struct nfile { - int type; - union node *next; - int fd; - union node *fname; - char *expfname; -}; - - -struct ndup { - int type; - union node *next; - int fd; - int dupfd; - union node *vname; -}; - - -struct nhere { - int type; - union node *next; - int fd; - union node *doc; -}; - - -struct nnot { - int type; - union node *com; -}; - - -union node { - int type; - struct nbinary nbinary; - struct ncmd ncmd; - struct npipe npipe; - struct nredir nredir; - struct nif nif; - struct nfor nfor; - struct ncase ncase; - struct nclist nclist; - struct narg narg; - struct nfile nfile; - struct ndup ndup; - struct nhere nhere; - struct nnot nnot; -}; - - -struct nodelist { - struct nodelist *next; - union node *n; -}; - - -union node *copyfunc(union node *); -void freefunc(union node *); diff --git a/sh/nodetypes b/sh/nodetypes deleted file mode 100644 index 4adebc0..0000000 --- a/sh/nodetypes +++ /dev/null @@ -1,143 +0,0 @@ -# $NetBSD: nodetypes,v 1.12 2003/08/22 11:22:23 agc 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 -# Kenneth Almquist. -# -# 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. -# -# @(#)nodetypes 8.2 (Berkeley) 5/4/95 - -# This file describes the nodes used in parse trees. Unindented lines -# contain a node type followed by a structure tag. Subsequent indented -# lines specify the fields of the structure. Several node types can share -# the same structure, in which case the fields of the structure should be -# specified only once. -# -# A field of a structure is described by the name of the field followed -# by a type. The currently implemented types are: -# nodeptr - a pointer to a node -# nodelist - a pointer to a list of nodes -# string - a pointer to a nul terminated string -# int - an integer -# other - any type that can be copied by assignment -# temp - a field that doesn't have to be copied when the node is copied -# The last two types should be followed by the text of a C declaration for -# the field. - -NSEMI nbinary # two commands separated by a semicolon - type int - ch1 nodeptr # the first child - ch2 nodeptr # the second child - -NCMD ncmd # a simple command - type int - backgnd int # set to run command in background - args nodeptr # the arguments - redirect nodeptr # list of file redirections - -NPIPE npipe # a pipeline - type int - backgnd int # set to run pipeline in background - cmdlist nodelist # the commands in the pipeline - -NREDIR nredir # redirection (of a complex command) - type int - n nodeptr # the command - redirect nodeptr # list of file redirections - -NBACKGND nredir # run command in background -NSUBSHELL nredir # run command in a subshell - -NAND nbinary # the && operator -NOR nbinary # the || operator - -NIF nif # the if statement. Elif clauses are handled - type int # using multiple if nodes. - test nodeptr # if test - ifpart nodeptr # then ifpart - elsepart nodeptr # else elsepart - -NWHILE nbinary # the while statement. First child is the test -NUNTIL nbinary # the until statement - -NFOR nfor # the for statement - type int - args nodeptr # for var in args - body nodeptr # do body; done - var string # the for variable - -NCASE ncase # a case statement - type int - expr nodeptr # the word to switch on - cases nodeptr # the list of cases (NCLIST nodes) - -NCLIST nclist # a case - type int - next nodeptr # the next case in list - pattern nodeptr # list of patterns for this case - body nodeptr # code to execute for this case - - -NDEFUN narg # define a function. The "next" field contains - # the body of the function. - -NARG narg # represents a word - type int - next nodeptr # next word in list - text string # the text of the word - backquote nodelist # list of commands in back quotes - -NTO nfile # fd> fname -NCLOBBER nfile # fd>| fname -NFROM nfile # fd< fname -NFROMTO nfile # fd<> fname -NAPPEND nfile # fd>> fname - type int - next nodeptr # next redirection in list - fd int # file descriptor being redirected - fname nodeptr # file name, in a NARG node - expfname temp char *expfname # actual file name - -NTOFD ndup # fd<&dupfd -NFROMFD ndup # fd>&dupfd - type int - next nodeptr # next redirection in list - fd int # file descriptor being redirected - dupfd int # file descriptor to duplicate - vname nodeptr # file name if fd>&$var - - -NHERE nhere # fd<<\! -NXHERE nhere # fd<<! - type int - next nodeptr # next redirection in list - fd int # file descriptor being redirected - doc nodeptr # input to command (NARG node) - -NNOT nnot # ! command (actually pipeline) - type int - com nodeptr diff --git a/sh/options.c b/sh/options.c deleted file mode 100644 index bc833c7..0000000 --- a/sh/options.c +++ /dev/null @@ -1,530 +0,0 @@ -/* $NetBSD: options.c,v 1.37 2004/10/30 19:29:27 christos 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 - * Kenneth Almquist. - * - * 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[] = "@(#)options.c 8.2 (Berkeley) 5/4/95"; -#else -__RCSID("$NetBSD: options.c,v 1.37 2004/10/30 19:29:27 christos Exp $"); -#endif -#endif /* not lint */ - -#include <signal.h> -#include <unistd.h> -#include <stdlib.h> - -#include "shell.h" -#define DEFINE_OPTIONS -#include "options.h" -#undef DEFINE_OPTIONS -#include "nodes.h" /* for other header files */ -#include "eval.h" -#include "jobs.h" -#include "input.h" -#include "output.h" -#include "trap.h" -#include "var.h" -#include "memalloc.h" -#include "error.h" -#include "mystring.h" -#ifndef SMALL -#include "myhistedit.h" -#endif -#include "show.h" - -char *arg0; /* value of $0 */ -struct shparam shellparam; /* current positional parameters */ -char **argptr; /* argument list for builtin commands */ -char *optionarg; /* set by nextopt (like getopt) */ -char *optptr; /* used by nextopt */ - -char *minusc; /* argument to -c option */ - - -STATIC void options(int); -STATIC void minus_o(char *, int); -STATIC void setoption(int, int); -STATIC int getopts(char *, char *, char **, char ***, char **); - - -/* - * Process the shell command line arguments. - */ - -void -procargs(int argc, char **argv) -{ - int i; - - argptr = argv; - if (argc > 0) - argptr++; - for (i = 0; i < NOPTS; i++) - optlist[i].val = 2; - options(1); - if (*argptr == NULL && minusc == NULL) - sflag = 1; - if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1)) - iflag = 1; - if (mflag == 2) - mflag = iflag; - for (i = 0; i < NOPTS; i++) - if (optlist[i].val == 2) - optlist[i].val = 0; -#if DEBUG == 2 - debug = 1; -#endif - arg0 = argv[0]; - if (sflag == 0 && minusc == NULL) { - commandname = argv[0]; - arg0 = *argptr++; - setinputfile(arg0, 0); - commandname = arg0; - } - /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */ - if (minusc != NULL) { - if (argptr == NULL || *argptr == NULL) - error("Bad -c option"); - minusc = *argptr++; - if (*argptr != 0) - arg0 = *argptr++; - } - - shellparam.p = argptr; - shellparam.reset = 1; - /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */ - while (*argptr) { - shellparam.nparam++; - argptr++; - } - optschanged(); -} - - -void -optschanged(void) -{ - setinteractive(iflag); -#ifdef WITH_HISTORY - histedit(); -#endif - setjobctl(mflag); -} - -/* - * Process shell options. The global variable argptr contains a pointer - * to the argument list; we advance it past the options. - */ - -STATIC void -options(int cmdline) -{ - static char empty[] = ""; - char *p; - int val; - int c; - - if (cmdline) - minusc = NULL; - while ((p = *argptr) != NULL) { - argptr++; - if ((c = *p++) == '-') { - val = 1; - if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) { - if (!cmdline) { - /* "-" means turn off -x and -v */ - if (p[0] == '\0') - xflag = vflag = 0; - /* "--" means reset params */ - else if (*argptr == NULL) - setparam(argptr); - } - break; /* "-" or "--" terminates options */ - } - } else if (c == '+') { - val = 0; - } else { - argptr--; - break; - } - while ((c = *p++) != '\0') { - if (c == 'c' && cmdline) { - /* command is after shell args*/ - minusc = empty; - } else if (c == 'o') { - minus_o(*argptr, val); - if (*argptr) - argptr++; - } else { - setoption(c, val); - } - } - } -} - -static void -set_opt_val(int i, int val) -{ - int j; - int flag; - - if (val && (flag = optlist[i].opt_set)) { - /* some options (eg vi/emacs) are mutually exclusive */ - for (j = 0; j < NOPTS; j++) - if (optlist[j].opt_set == flag) - optlist[j].val = 0; - } - optlist[i].val = val; -#ifdef DEBUG - if (&optlist[i].val == &debug) - opentrace(); -#endif -} - -STATIC void -minus_o(char *name, int val) -{ - int i; - - if (name == NULL) { - out1str("Current option settings\n"); - for (i = 0; i < NOPTS; i++) - out1fmt("%-16s%s\n", optlist[i].name, - optlist[i].val ? "on" : "off"); - } else { - for (i = 0; i < NOPTS; i++) - if (equal(name, optlist[i].name)) { - set_opt_val(i, val); - return; - } - error("Illegal option -o %s", name); - } -} - - -STATIC void -setoption(int flag, int val) -{ - int i; - - for (i = 0; i < NOPTS; i++) - if (optlist[i].letter == flag) { - set_opt_val( i, val ); - return; - } - error("Illegal option -%c", flag); - /* NOTREACHED */ -} - - - -#ifdef mkinit -INCLUDE "options.h" - -SHELLPROC { - int i; - - for (i = 0; optlist[i].name; i++) - optlist[i].val = 0; - optschanged(); - -} -#endif - - -/* - * Set the shell parameters. - */ - -void -setparam(char **argv) -{ - char **newparam; - char **ap; - int nparam; - - for (nparam = 0 ; argv[nparam] ; nparam++); - ap = newparam = ckmalloc((nparam + 1) * sizeof *ap); - while (*argv) { - *ap++ = savestr(*argv++); - } - *ap = NULL; - freeparam(&shellparam); - shellparam.malloc = 1; - shellparam.nparam = nparam; - shellparam.p = newparam; - shellparam.optnext = NULL; -} - - -/* - * Free the list of positional parameters. - */ - -void -freeparam(volatile struct shparam *param) -{ - char **ap; - - if (param->malloc) { - for (ap = param->p ; *ap ; ap++) - ckfree(*ap); - ckfree(param->p); - } -} - - - -/* - * The shift builtin command. - */ - -int -shiftcmd(int argc, char **argv) -{ - int n; - char **ap1, **ap2; - - n = 1; - if (argc > 1) - n = number(argv[1]); - if (n > shellparam.nparam) - error("can't shift that many"); - INTOFF; - shellparam.nparam -= n; - for (ap1 = shellparam.p ; --n >= 0 ; ap1++) { - if (shellparam.malloc) - ckfree(*ap1); - } - ap2 = shellparam.p; - while ((*ap2++ = *ap1++) != NULL); - shellparam.optnext = NULL; - INTON; - return 0; -} - - - -/* - * The set command builtin. - */ - -int -setcmd(int argc, char **argv) -{ - if (argc == 1) - return showvars(0, 0, 1); - INTOFF; - options(0); - optschanged(); - if (*argptr != NULL) { - setparam(argptr); - } - INTON; - return 0; -} - - -void -getoptsreset(value) - const char *value; -{ - if (number(value) == 1) { - shellparam.optnext = NULL; - shellparam.reset = 1; - } -} - -/* - * The getopts builtin. Shellparam.optnext points to the next argument - * to be processed. Shellparam.optptr points to the next character to - * be processed in the current argument. If shellparam.optnext is NULL, - * then it's the first time getopts has been called. - */ - -int -getoptscmd(int argc, char **argv) -{ - char **optbase; - - if (argc < 3) - error("usage: getopts optstring var [arg]"); - else if (argc == 3) - optbase = shellparam.p; - else - optbase = &argv[3]; - - if (shellparam.reset == 1) { - shellparam.optnext = optbase; - shellparam.optptr = NULL; - shellparam.reset = 0; - } - - return getopts(argv[1], argv[2], optbase, &shellparam.optnext, - &shellparam.optptr); -} - -STATIC int -getopts(char *optstr, char *optvar, char **optfirst, char ***optnext, char **optpptr) -{ - char *p, *q; - char c = '?'; - int done = 0; - int ind = 0; - int err = 0; - char s[12]; - - if ((p = *optpptr) == NULL || *p == '\0') { - /* Current word is done, advance */ - if (*optnext == NULL) - return 1; - p = **optnext; - if (p == NULL || *p != '-' || *++p == '\0') { -atend: - ind = *optnext - optfirst + 1; - *optnext = NULL; - p = NULL; - done = 1; - goto out; - } - (*optnext)++; - if (p[0] == '-' && p[1] == '\0') /* check for "--" */ - goto atend; - } - - c = *p++; - for (q = optstr; *q != c; ) { - if (*q == '\0') { - if (optstr[0] == ':') { - s[0] = c; - s[1] = '\0'; - err |= setvarsafe("OPTARG", s, 0); - } else { - outfmt(&errout, "Illegal option -%c\n", c); - (void) unsetvar("OPTARG", 0); - } - c = '?'; - goto bad; - } - if (*++q == ':') - q++; - } - - if (*++q == ':') { - if (*p == '\0' && (p = **optnext) == NULL) { - if (optstr[0] == ':') { - s[0] = c; - s[1] = '\0'; - err |= setvarsafe("OPTARG", s, 0); - c = ':'; - } else { - outfmt(&errout, "No arg for -%c option\n", c); - (void) unsetvar("OPTARG", 0); - c = '?'; - } - goto bad; - } - - if (p == **optnext) - (*optnext)++; - err |= setvarsafe("OPTARG", p, 0); - p = NULL; - } else - err |= setvarsafe("OPTARG", "", 0); - ind = *optnext - optfirst + 1; - goto out; - -bad: - ind = 1; - *optnext = NULL; - p = NULL; -out: - *optpptr = p; - fmtstr(s, sizeof(s), "%d", ind); - err |= setvarsafe("OPTIND", s, VNOFUNC); - s[0] = c; - s[1] = '\0'; - err |= setvarsafe(optvar, s, 0); - if (err) { - *optnext = NULL; - *optpptr = NULL; - flushall(); - exraise(EXERROR); - } - return done; -} - -/* - * XXX - should get rid of. have all builtins use getopt(3). the - * library getopt must have the BSD extension static variable "optreset" - * otherwise it can't be used within the shell safely. - * - * Standard option processing (a la getopt) for builtin routines. The - * only argument that is passed to nextopt is the option string; the - * other arguments are unnecessary. It return the character, or '\0' on - * end of input. - */ - -int -nextopt(const char *optstring) -{ - char *p; - const char *q; - char c; - - if ((p = optptr) == NULL || *p == '\0') { - p = *argptr; - if (p == NULL || *p != '-' || *++p == '\0') - return '\0'; - argptr++; - if (p[0] == '-' && p[1] == '\0') /* check for "--" */ - return '\0'; - } - c = *p++; - for (q = optstring ; *q != c ; ) { - if (*q == '\0') - error("Illegal option -%c", c); - if (*++q == ':') - q++; - } - if (*++q == ':') { - if (*p == '\0' && (p = *argptr++) == NULL) - error("No arg for -%c option", c); - optionarg = p; - p = NULL; - } - optptr = p; - return c; -} diff --git a/sh/options.h b/sh/options.h deleted file mode 100644 index 4cc7dbe..0000000 --- a/sh/options.h +++ /dev/null @@ -1,131 +0,0 @@ -/* $NetBSD: options.h,v 1.17 2003/08/07 09:05:36 agc 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 - * Kenneth Almquist. - * - * 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. - * - * @(#)options.h 8.2 (Berkeley) 5/4/95 - */ - -struct shparam { - int nparam; /* # of positional parameters (without $0) */ - unsigned char malloc; /* if parameter list dynamically allocated */ - unsigned char reset; /* if getopts has been reset */ - char **p; /* parameter list */ - char **optnext; /* next parameter to be processed by getopts */ - char *optptr; /* used by getopts */ -}; - - -struct optent { - const char *name; /* for set -o <name> */ - const char letter; /* set [+/-]<letter> and $- */ - const char opt_set; /* mutually exclusive option set */ - char val; /* value of <letter>flag */ -}; - -/* Those marked [U] are required by posix, but have no effect! */ - -#ifdef DEFINE_OPTIONS -#define DEF_OPTS(name, letter, opt_set) {name, letter, opt_set, 0}, -struct optent optlist[] = { -#else -#define DEF_OPTS(name, letter, opt_set) -#endif -#define DEF_OPT(name,letter) DEF_OPTS(name, letter, 0) - -DEF_OPT( "errexit", 'e' ) /* exit on error */ -#define eflag optlist[0].val -DEF_OPT( "noglob", 'f' ) /* no pathname expansion */ -#define fflag optlist[1].val -DEF_OPT( "ignoreeof", 'I' ) /* do not exit on EOF */ -#define Iflag optlist[2].val -DEF_OPT( "interactive",'i' ) /* interactive shell */ -#define iflag optlist[3].val -DEF_OPT( "monitor", 'm' ) /* job control */ -#define mflag optlist[4].val -DEF_OPT( "noexec", 'n' ) /* [U] do not exec commands */ -#define nflag optlist[5].val -DEF_OPT( "stdin", 's' ) /* read from stdin */ -#define sflag optlist[6].val -DEF_OPT( "xtrace", 'x' ) /* trace after expansion */ -#define xflag optlist[7].val -DEF_OPT( "verbose", 'v' ) /* trace read input */ -#define vflag optlist[8].val -DEF_OPTS( "vi", 'V', 'V' ) /* vi style editing */ -#define Vflag optlist[9].val -DEF_OPTS( "emacs", 'E', 'V' ) /* emacs style editing */ -#define Eflag optlist[10].val -DEF_OPT( "noclobber", 'C' ) /* do not overwrite files with > */ -#define Cflag optlist[11].val -DEF_OPT( "allexport", 'a' ) /* export all variables */ -#define aflag optlist[12].val -DEF_OPT( "notify", 'b' ) /* [U] report completion of background jobs */ -#define bflag optlist[13].val -DEF_OPT( "nounset", 'u' ) /* error expansion of unset variables */ -#define uflag optlist[14].val -DEF_OPT( "quietprofile", 'q' ) -#define qflag optlist[15].val -DEF_OPT( "nolog", 0 ) /* [U] no functon defs in command history */ -#define nolog optlist[16].val -DEF_OPT( "cdprint", 0 ) /* always print result of cd */ -#define cdprint optlist[17].val -#ifdef DEBUG -DEF_OPT( "debug", 0 ) /* enable debug prints */ -#define debug optlist[18].val -#endif - -#ifdef DEFINE_OPTIONS - { 0, 0, 0, 0 }, -}; -#define NOPTS (sizeof optlist / sizeof optlist[0] - 1) -int sizeof_optlist = sizeof optlist; -#else -extern struct optent optlist[]; -extern int sizeof_optlist; -#endif - - -extern char *minusc; /* argument to -c option */ -extern char *arg0; /* $0 */ -extern struct shparam shellparam; /* $@ */ -extern char **argptr; /* argument list for builtin commands */ -extern char *optionarg; /* set by nextopt */ -extern char *optptr; /* used by nextopt */ - -void procargs(int, char **); -void optschanged(void); -void setparam(char **); -void freeparam(volatile struct shparam *); -int shiftcmd(int, char **); -int setcmd(int, char **); -int getoptscmd(int, char **); -int nextopt(const char *); -void getoptsreset(const char *); diff --git a/sh/output.c b/sh/output.c deleted file mode 100644 index b0e669e..0000000 --- a/sh/output.c +++ /dev/null @@ -1,516 +0,0 @@ -/* $NetBSD: output.c,v 1.28 2003/08/07 09:05:36 agc 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 - * Kenneth Almquist. - * - * 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[] = "@(#)output.c 8.2 (Berkeley) 5/4/95"; -#else -__RCSID("$NetBSD: output.c,v 1.28 2003/08/07 09:05:36 agc Exp $"); -#endif -#endif /* not lint */ - -/* - * Shell output routines. We use our own output routines because: - * When a builtin command is interrupted we have to discard - * any pending output. - * When a builtin command appears in back quotes, we want to - * save the output of the command in a region obtained - * via malloc, rather than doing a fork and reading the - * output of the command via a pipe. - * Our output routines may be smaller than the stdio routines. - */ - -#include <sys/types.h> /* quad_t */ -#include <sys/param.h> /* BSD4_4 */ -#include <sys/ioctl.h> - -#include <stdio.h> /* defines BUFSIZ */ -#include <string.h> -#include <errno.h> -#include <unistd.h> -#include <stdlib.h> - -#include "shell.h" -#include "syntax.h" -#include "output.h" -#include "memalloc.h" -#include "error.h" - - -#define OUTBUFSIZ BUFSIZ -#define BLOCK_OUT -2 /* output to a fixed block of memory */ -#define MEM_OUT -3 /* output to dynamically allocated memory */ -#define OUTPUT_ERR 01 /* error occurred on output */ - - -struct output output = {NULL, 0, NULL, OUTBUFSIZ, 1, 0}; -struct output errout = {NULL, 0, NULL, 100, 2, 0}; -struct output memout = {NULL, 0, NULL, 0, MEM_OUT, 0}; -struct output *out1 = &output; -struct output *out2 = &errout; - - - -#ifdef mkinit - -INCLUDE "output.h" -INCLUDE "memalloc.h" - -RESET { - out1 = &output; - out2 = &errout; - if (memout.buf != NULL) { - ckfree(memout.buf); - memout.buf = NULL; - } -} - -#endif - - -#ifdef notdef /* no longer used */ -/* - * Set up an output file to write to memory rather than a file. - */ - -void -open_mem(char *block, int length, struct output *file) -{ - file->nextc = block; - file->nleft = --length; - file->fd = BLOCK_OUT; - file->flags = 0; -} -#endif - - -void -out1str(const char *p) -{ - outstr(p, out1); -} - - -void -out2str(const char *p) -{ - outstr(p, out2); -} - - -void -outstr(const char *p, struct output *file) -{ - while (*p) - outc(*p++, file); - if (file == out2) - flushout(file); -} - - -char out_junk[16]; - - -void -emptyoutbuf(struct output *dest) -{ - int offset; - - if (dest->fd == BLOCK_OUT) { - dest->nextc = out_junk; - dest->nleft = sizeof out_junk; - dest->flags |= OUTPUT_ERR; - } else if (dest->buf == NULL) { - INTOFF; - dest->buf = ckmalloc(dest->bufsize); - dest->nextc = dest->buf; - dest->nleft = dest->bufsize; - INTON; - } else if (dest->fd == MEM_OUT) { - offset = dest->bufsize; - INTOFF; - dest->bufsize <<= 1; - dest->buf = ckrealloc(dest->buf, dest->bufsize); - dest->nleft = dest->bufsize - offset; - dest->nextc = dest->buf + offset; - INTON; - } else { - flushout(dest); - } - dest->nleft--; -} - - -void -flushall(void) -{ - flushout(&output); - flushout(&errout); -} - - -void -flushout(struct output *dest) -{ - - if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0) - return; - if (xwrite(dest->fd, dest->buf, dest->nextc - dest->buf) < 0) - dest->flags |= OUTPUT_ERR; - dest->nextc = dest->buf; - dest->nleft = dest->bufsize; -} - - -void -freestdout(void) -{ - INTOFF; - if (output.buf) { - ckfree(output.buf); - output.buf = NULL; - output.nleft = 0; - } - INTON; -} - - -void -outfmt(struct output *file, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - doformat(file, fmt, ap); - va_end(ap); -} - - -void -out1fmt(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - doformat(out1, fmt, ap); - va_end(ap); -} - -void -dprintf(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - doformat(out2, fmt, ap); - va_end(ap); - flushout(out2); -} - -void -fmtstr(char *outbuf, size_t length, const char *fmt, ...) -{ - va_list ap; - struct output strout; - - va_start(ap, fmt); - strout.nextc = outbuf; - strout.nleft = length; - strout.fd = BLOCK_OUT; - strout.flags = 0; - doformat(&strout, fmt, ap); - outc('\0', &strout); - if (strout.flags & OUTPUT_ERR) - outbuf[length - 1] = '\0'; - va_end(ap); -} - -/* - * Formatted output. This routine handles a subset of the printf formats: - * - Formats supported: d, u, o, p, X, s, and c. - * - The x format is also accepted but is treated like X. - * - The l, ll and q modifiers are accepted. - * - The - and # flags are accepted; # only works with the o format. - * - Width and precision may be specified with any format except c. - * - An * may be given for the width or precision. - * - The obsolete practice of preceding the width with a zero to get - * zero padding is not supported; use the precision field. - * - A % may be printed by writing %% in the format string. - */ - -#define TEMPSIZE 24 - -#ifdef BSD4_4 -#define HAVE_VASPRINTF 1 -#endif - -void -doformat(struct output *dest, const char *f, va_list ap) -{ -#if HAVE_VASPRINTF - char *s; - - vasprintf(&s, f, ap); - outstr(s, dest); - free(s); -#else /* !HAVE_VASPRINTF */ - static const char digit[] = "0123456789ABCDEF"; - char c; - char temp[TEMPSIZE]; - int flushleft; - int sharp; - int width; - int prec; - int islong; - int isquad; - char *p; - int sign; -#ifdef BSD4_4 - quad_t l; - u_quad_t num; -#else - long l; - u_long num; -#endif - unsigned base; - int len; - int size; - int pad; - - while ((c = *f++) != '\0') { - if (c != '%') { - outc(c, dest); - continue; - } - flushleft = 0; - sharp = 0; - width = 0; - prec = -1; - islong = 0; - isquad = 0; - for (;;) { - if (*f == '-') - flushleft++; - else if (*f == '#') - sharp++; - else - break; - f++; - } - if (*f == '*') { - width = va_arg(ap, int); - f++; - } else { - while (is_digit(*f)) { - width = 10 * width + digit_val(*f++); - } - } - if (*f == '.') { - if (*++f == '*') { - prec = va_arg(ap, int); - f++; - } else { - prec = 0; - while (is_digit(*f)) { - prec = 10 * prec + digit_val(*f++); - } - } - } - if (*f == 'l') { - f++; - if (*f == 'l') { - isquad++; - f++; - } else - islong++; - } else if (*f == 'q') { - isquad++; - f++; - } - switch (*f) { - case 'd': -#ifdef BSD4_4 - if (isquad) - l = va_arg(ap, quad_t); - else -#endif - if (islong) - l = va_arg(ap, long); - else - l = va_arg(ap, int); - sign = 0; - num = l; - if (l < 0) { - num = -l; - sign = 1; - } - base = 10; - goto number; - case 'u': - base = 10; - goto uns_number; - case 'o': - base = 8; - goto uns_number; - case 'p': - outc('0', dest); - outc('x', dest); - /*FALLTHROUGH*/ - case 'x': - /* we don't implement 'x'; treat like 'X' */ - case 'X': - base = 16; -uns_number: /* an unsigned number */ - sign = 0; -#ifdef BSD4_4 - if (isquad) - num = va_arg(ap, u_quad_t); - else -#endif - if (islong) - num = va_arg(ap, unsigned long); - else - num = va_arg(ap, unsigned int); -number: /* process a number */ - p = temp + TEMPSIZE - 1; - *p = '\0'; - while (num) { - *--p = digit[num % base]; - num /= base; - } - len = (temp + TEMPSIZE - 1) - p; - if (prec < 0) - prec = 1; - if (sharp && *f == 'o' && prec <= len) - prec = len + 1; - pad = 0; - if (width) { - size = len; - if (size < prec) - size = prec; - size += sign; - pad = width - size; - if (flushleft == 0) { - while (--pad >= 0) - outc(' ', dest); - } - } - if (sign) - outc('-', dest); - prec -= len; - while (--prec >= 0) - outc('0', dest); - while (*p) - outc(*p++, dest); - while (--pad >= 0) - outc(' ', dest); - break; - case 's': - p = va_arg(ap, char *); - pad = 0; - if (width) { - len = strlen(p); - if (prec >= 0 && len > prec) - len = prec; - pad = width - len; - if (flushleft == 0) { - while (--pad >= 0) - outc(' ', dest); - } - } - prec++; - while (--prec != 0 && *p) - outc(*p++, dest); - while (--pad >= 0) - outc(' ', dest); - break; - case 'c': - c = va_arg(ap, int); - outc(c, dest); - break; - default: - outc(*f, dest); - break; - } - f++; - } -#endif /* !HAVE_VASPRINTF */ -} - - - -/* - * Version of write which resumes after a signal is caught. - */ - -int -xwrite(int fd, char *buf, int nbytes) -{ - int ntry; - int i; - int n; - - n = nbytes; - ntry = 0; - for (;;) { - i = write(fd, buf, n); - if (i > 0) { - if ((n -= i) <= 0) - return nbytes; - buf += i; - ntry = 0; - } else if (i == 0) { - if (++ntry > 10) - return nbytes - n; - } else if (errno != EINTR) { - return -1; - } - } -} - - -/* - * Version of ioctl that retries after a signal is caught. - * XXX unused function - */ - -int -xioctl(int fd, unsigned long request, char *arg) -{ - int i; - - while ((i = ioctl(fd, request, arg)) == -1 && errno == EINTR); - return i; -} diff --git a/sh/output.h b/sh/output.h deleted file mode 100644 index 9a199a0..0000000 --- a/sh/output.h +++ /dev/null @@ -1,81 +0,0 @@ -/* $NetBSD: output.h,v 1.17 2003/08/07 09:05:36 agc 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 - * Kenneth Almquist. - * - * 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. - * - * @(#)output.h 8.2 (Berkeley) 5/4/95 - */ - -#ifndef OUTPUT_INCL - -#include <stdarg.h> - -struct output { - char *nextc; - int nleft; - char *buf; - int bufsize; - short fd; - short flags; -}; - -extern struct output output; -extern struct output errout; -extern struct output memout; -extern struct output *out1; -extern struct output *out2; - -void open_mem(char *, int, struct output *); -void out1str(const char *); -void out2str(const char *); -void outstr(const char *, struct output *); -void emptyoutbuf(struct output *); -void flushall(void); -void flushout(struct output *); -void freestdout(void); -void outfmt(struct output *, const char *, ...) - __attribute__((__format__(__printf__,2,3))); -void out1fmt(const char *, ...) - __attribute__((__format__(__printf__,1,2))); -void dprintf(const char *, ...) - __attribute__((__format__(__printf__,1,2))); -void fmtstr(char *, size_t, const char *, ...) - __attribute__((__format__(__printf__,3,4))); -void doformat(struct output *, const char *, va_list); -int xwrite(int, char *, int); -int xioctl(int, unsigned long, char *); - -#define outc(c, file) (--(file)->nleft < 0? (emptyoutbuf(file), *(file)->nextc++ = (c)) : (*(file)->nextc++ = (c))) -#define out1c(c) outc(c, out1); -#define out2c(c) outc(c, out2); - -#define OUTPUT_INCL -#endif diff --git a/sh/parser.c b/sh/parser.c deleted file mode 100644 index faf0268..0000000 --- a/sh/parser.c +++ /dev/null @@ -1,1654 +0,0 @@ -/* $NetBSD: parser.c,v 1.57 2004/06/27 10:27:57 dsl 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 - * Kenneth Almquist. - * - * 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[] = "@(#)parser.c 8.7 (Berkeley) 5/16/95"; -#else -__RCSID("$NetBSD: parser.c,v 1.57 2004/06/27 10:27:57 dsl Exp $"); -#endif -#endif /* not lint */ - -#include <stdlib.h> - -#include "shell.h" -#include "parser.h" -#include "nodes.h" -#include "expand.h" /* defines rmescapes() */ -#include "eval.h" /* defines commandname */ -#include "redir.h" /* defines copyfd() */ -#include "syntax.h" -#include "options.h" -#include "input.h" -#include "output.h" -#include "var.h" -#include "error.h" -#include "memalloc.h" -#include "mystring.h" -#include "alias.h" -#include "show.h" -#ifndef SMALL -#include "myhistedit.h" -#endif - -/* - * Shell command parser. - */ - -#define EOFMARKLEN 79 - -/* values returned by readtoken */ -#include "token.h" - -#define OPENBRACE '{' -#define CLOSEBRACE '}' - - -struct heredoc { - struct heredoc *next; /* next here document in list */ - union node *here; /* redirection node */ - char *eofmark; /* string indicating end of input */ - int striptabs; /* if set, strip leading tabs */ -}; - - - -static int noalias = 0; /* when set, don't handle aliases */ -struct heredoc *heredoclist; /* list of here documents to read */ -int parsebackquote; /* nonzero if we are inside backquotes */ -int doprompt; /* if set, prompt the user */ -int needprompt; /* true if interactive and at start of line */ -int lasttoken; /* last token read */ -MKINIT int tokpushback; /* last token pushed back */ -char *wordtext; /* text of last word returned by readtoken */ -MKINIT int checkkwd; /* 1 == check for kwds, 2 == also eat newlines */ -struct nodelist *backquotelist; -union node *redirnode; -struct heredoc *heredoc; -int quoteflag; /* set if (part of) last token was quoted */ -int startlinno; /* line # where last token started */ - - -STATIC union node *list(int); -STATIC union node *andor(void); -STATIC union node *pipeline(void); -STATIC union node *command(void); -STATIC union node *simplecmd(union node **, union node *); -STATIC union node *makename(void); -STATIC void parsefname(void); -STATIC void parseheredoc(void); -STATIC int peektoken(void); -STATIC int readtoken(void); -STATIC int xxreadtoken(void); -STATIC int readtoken1(int, char const *, char *, int); -STATIC int noexpand(char *); -STATIC void synexpect(int) __attribute__((__noreturn__)); -STATIC void synerror(const char *) __attribute__((__noreturn__)); -STATIC void setprompt(int); - - -/* - * Read and parse a command. Returns NEOF on end of file. (NULL is a - * valid parse tree indicating a blank line.) - */ - -union node * -parsecmd(int interact) -{ - int t; - - tokpushback = 0; - doprompt = interact; - if (doprompt) - setprompt(1); - else - setprompt(0); - needprompt = 0; - t = readtoken(); - if (t == TEOF) - return NEOF; - if (t == TNL) - return NULL; - tokpushback++; - return list(1); -} - - -STATIC union node * -list(int nlflag) -{ - union node *n1, *n2, *n3; - int tok; - - checkkwd = 2; - if (nlflag == 0 && tokendlist[peektoken()]) - return NULL; - n1 = NULL; - for (;;) { - n2 = andor(); - tok = readtoken(); - if (tok == TBACKGND) { - if (n2->type == NCMD || n2->type == NPIPE) { - n2->ncmd.backgnd = 1; - } else if (n2->type == NREDIR) { - n2->type = NBACKGND; - } else { - n3 = (union node *)stalloc(sizeof (struct nredir)); - n3->type = NBACKGND; - n3->nredir.n = n2; - n3->nredir.redirect = NULL; - n2 = n3; - } - } - if (n1 == NULL) { - n1 = n2; - } - else { - n3 = (union node *)stalloc(sizeof (struct nbinary)); - n3->type = NSEMI; - n3->nbinary.ch1 = n1; - n3->nbinary.ch2 = n2; - n1 = n3; - } - switch (tok) { - case TBACKGND: - case TSEMI: - tok = readtoken(); - /* fall through */ - case TNL: - if (tok == TNL) { - parseheredoc(); - if (nlflag) - return n1; - } else { - tokpushback++; - } - checkkwd = 2; - if (tokendlist[peektoken()]) - return n1; - break; - case TEOF: - if (heredoclist) - parseheredoc(); - else - pungetc(); /* push back EOF on input */ - return n1; - default: - if (nlflag) - synexpect(-1); - tokpushback++; - return n1; - } - } -} - - - -STATIC union node * -andor(void) -{ - union node *n1, *n2, *n3; - int t; - - n1 = pipeline(); - for (;;) { - if ((t = readtoken()) == TAND) { - t = NAND; - } else if (t == TOR) { - t = NOR; - } else { - tokpushback++; - return n1; - } - n2 = pipeline(); - n3 = (union node *)stalloc(sizeof (struct nbinary)); - n3->type = t; - n3->nbinary.ch1 = n1; - n3->nbinary.ch2 = n2; - n1 = n3; - } -} - - - -STATIC union node * -pipeline(void) -{ - union node *n1, *n2, *pipenode; - struct nodelist *lp, *prev; - int negate; - - negate = 0; - TRACE(("pipeline: entered\n")); - while (readtoken() == TNOT) - negate = !negate; - tokpushback++; - n1 = command(); - if (readtoken() == TPIPE) { - pipenode = (union node *)stalloc(sizeof (struct npipe)); - pipenode->type = NPIPE; - pipenode->npipe.backgnd = 0; - lp = (struct nodelist *)stalloc(sizeof (struct nodelist)); - pipenode->npipe.cmdlist = lp; - lp->n = n1; - do { - prev = lp; - lp = (struct nodelist *)stalloc(sizeof (struct nodelist)); - lp->n = command(); - prev->next = lp; - } while (readtoken() == TPIPE); - lp->next = NULL; - n1 = pipenode; - } - tokpushback++; - if (negate) { - n2 = (union node *)stalloc(sizeof (struct nnot)); - n2->type = NNOT; - n2->nnot.com = n1; - return n2; - } else - return n1; -} - - - -STATIC union node * -command(void) -{ - union node *n1, *n2; - union node *ap, **app; - union node *cp, **cpp; - union node *redir, **rpp; - int t, negate = 0; - - checkkwd = 2; - redir = NULL; - n1 = NULL; - rpp = &redir; - - /* Check for redirection which may precede command */ - while (readtoken() == TREDIR) { - *rpp = n2 = redirnode; - rpp = &n2->nfile.next; - parsefname(); - } - tokpushback++; - - while (readtoken() == TNOT) { - TRACE(("command: TNOT recognized\n")); - negate = !negate; - } - tokpushback++; - - switch (readtoken()) { - case TIF: - n1 = (union node *)stalloc(sizeof (struct nif)); - n1->type = NIF; - n1->nif.test = list(0); - if (readtoken() != TTHEN) - synexpect(TTHEN); - n1->nif.ifpart = list(0); - n2 = n1; - while (readtoken() == TELIF) { - n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif)); - n2 = n2->nif.elsepart; - n2->type = NIF; - n2->nif.test = list(0); - if (readtoken() != TTHEN) - synexpect(TTHEN); - n2->nif.ifpart = list(0); - } - if (lasttoken == TELSE) - n2->nif.elsepart = list(0); - else { - n2->nif.elsepart = NULL; - tokpushback++; - } - if (readtoken() != TFI) - synexpect(TFI); - checkkwd = 1; - break; - case TWHILE: - case TUNTIL: { - int got; - n1 = (union node *)stalloc(sizeof (struct nbinary)); - n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL; - n1->nbinary.ch1 = list(0); - if ((got=readtoken()) != TDO) { -TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); - synexpect(TDO); - } - n1->nbinary.ch2 = list(0); - if (readtoken() != TDONE) - synexpect(TDONE); - checkkwd = 1; - break; - } - case TFOR: - if (readtoken() != TWORD || quoteflag || ! goodname(wordtext)) - synerror("Bad for loop variable"); - n1 = (union node *)stalloc(sizeof (struct nfor)); - n1->type = NFOR; - n1->nfor.var = wordtext; - if (readtoken() == TWORD && ! quoteflag && equal(wordtext, "in")) { - app = ≈ - while (readtoken() == TWORD) { - n2 = (union node *)stalloc(sizeof (struct narg)); - n2->type = NARG; - n2->narg.text = wordtext; - n2->narg.backquote = backquotelist; - *app = n2; - app = &n2->narg.next; - } - *app = NULL; - n1->nfor.args = ap; - if (lasttoken != TNL && lasttoken != TSEMI) - synexpect(-1); - } else { - static char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE, - '@', '=', '\0'}; - n2 = (union node *)stalloc(sizeof (struct narg)); - n2->type = NARG; - n2->narg.text = argvars; - n2->narg.backquote = NULL; - n2->narg.next = NULL; - n1->nfor.args = n2; - /* - * Newline or semicolon here is optional (but note - * that the original Bourne shell only allowed NL). - */ - if (lasttoken != TNL && lasttoken != TSEMI) - tokpushback++; - } - checkkwd = 2; - if ((t = readtoken()) == TDO) - t = TDONE; - else if (t == TBEGIN) - t = TEND; - else - synexpect(-1); - n1->nfor.body = list(0); - if (readtoken() != t) - synexpect(t); - checkkwd = 1; - break; - case TCASE: - n1 = (union node *)stalloc(sizeof (struct ncase)); - n1->type = NCASE; - if (readtoken() != TWORD) - synexpect(TWORD); - n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg)); - n2->type = NARG; - n2->narg.text = wordtext; - n2->narg.backquote = backquotelist; - n2->narg.next = NULL; - while (readtoken() == TNL); - if (lasttoken != TWORD || ! equal(wordtext, "in")) - synerror("expecting \"in\""); - cpp = &n1->ncase.cases; - noalias = 1; - checkkwd = 2, readtoken(); - do { - *cpp = cp = (union node *)stalloc(sizeof (struct nclist)); - cp->type = NCLIST; - app = &cp->nclist.pattern; - for (;;) { - *app = ap = (union node *)stalloc(sizeof (struct narg)); - ap->type = NARG; - ap->narg.text = wordtext; - ap->narg.backquote = backquotelist; - if (checkkwd = 2, readtoken() != TPIPE) - break; - app = &ap->narg.next; - readtoken(); - } - ap->narg.next = NULL; - noalias = 0; - if (lasttoken != TRP) { - synexpect(TRP); - } - cp->nclist.body = list(0); - - checkkwd = 2; - if ((t = readtoken()) != TESAC) { - if (t != TENDCASE) { - noalias = 0; - synexpect(TENDCASE); - } else { - noalias = 1; - checkkwd = 2; - readtoken(); - } - } - cpp = &cp->nclist.next; - } while(lasttoken != TESAC); - noalias = 0; - *cpp = NULL; - checkkwd = 1; - break; - case TLP: - n1 = (union node *)stalloc(sizeof (struct nredir)); - n1->type = NSUBSHELL; - n1->nredir.n = list(0); - n1->nredir.redirect = NULL; - if (readtoken() != TRP) - synexpect(TRP); - checkkwd = 1; - break; - case TBEGIN: - n1 = list(0); - if (readtoken() != TEND) - synexpect(TEND); - checkkwd = 1; - break; - /* Handle an empty command like other simple commands. */ - case TSEMI: - /* - * An empty command before a ; doesn't make much sense, and - * should certainly be disallowed in the case of `if ;'. - */ - if (!redir) - synexpect(-1); - case TAND: - case TOR: - case TNL: - case TEOF: - case TWORD: - case TRP: - tokpushback++; - n1 = simplecmd(rpp, redir); - goto checkneg; - default: - synexpect(-1); - /* NOTREACHED */ - } - - /* Now check for redirection which may follow command */ - while (readtoken() == TREDIR) { - *rpp = n2 = redirnode; - rpp = &n2->nfile.next; - parsefname(); - } - tokpushback++; - *rpp = NULL; - if (redir) { - if (n1->type != NSUBSHELL) { - n2 = (union node *)stalloc(sizeof (struct nredir)); - n2->type = NREDIR; - n2->nredir.n = n1; - n1 = n2; - } - n1->nredir.redirect = redir; - } - -checkneg: - if (negate) { - n2 = (union node *)stalloc(sizeof (struct nnot)); - n2->type = NNOT; - n2->nnot.com = n1; - return n2; - } - else - return n1; -} - - -STATIC union node * -simplecmd(union node **rpp, union node *redir) -{ - union node *args, **app; - union node **orig_rpp = rpp; - union node *n = NULL, *n2; - int negate = 0; - - /* If we don't have any redirections already, then we must reset */ - /* rpp to be the address of the local redir variable. */ - if (redir == 0) - rpp = &redir; - - args = NULL; - app = &args; - /* - * We save the incoming value, because we need this for shell - * functions. There can not be a redirect or an argument between - * the function name and the open parenthesis. - */ - orig_rpp = rpp; - - while (readtoken() == TNOT) { - TRACE(("command: TNOT recognized\n")); - negate = !negate; - } - tokpushback++; - - for (;;) { - if (readtoken() == TWORD) { - n = (union node *)stalloc(sizeof (struct narg)); - n->type = NARG; - n->narg.text = wordtext; - n->narg.backquote = backquotelist; - *app = n; - app = &n->narg.next; - } else if (lasttoken == TREDIR) { - *rpp = n = redirnode; - rpp = &n->nfile.next; - parsefname(); /* read name of redirection file */ - } else if (lasttoken == TLP && app == &args->narg.next - && rpp == orig_rpp) { - /* We have a function */ - if (readtoken() != TRP) - synexpect(TRP); -#ifdef notdef - if (! goodname(n->narg.text)) - synerror("Bad function name"); -#endif - n->type = NDEFUN; - n->narg.next = command(); - goto checkneg; - } else { - tokpushback++; - break; - } - } - *app = NULL; - *rpp = NULL; - n = (union node *)stalloc(sizeof (struct ncmd)); - n->type = NCMD; - n->ncmd.backgnd = 0; - n->ncmd.args = args; - n->ncmd.redirect = redir; - -checkneg: - if (negate) { - n2 = (union node *)stalloc(sizeof (struct nnot)); - n2->type = NNOT; - n2->nnot.com = n; - return n2; - } - else - return n; -} - -STATIC union node * -makename(void) -{ - union node *n; - - n = (union node *)stalloc(sizeof (struct narg)); - n->type = NARG; - n->narg.next = NULL; - n->narg.text = wordtext; - n->narg.backquote = backquotelist; - return n; -} - -void fixredir(union node *n, const char *text, int err) - { - TRACE(("Fix redir %s %d\n", text, err)); - if (!err) - n->ndup.vname = NULL; - - if (is_digit(text[0]) && text[1] == '\0') - n->ndup.dupfd = digit_val(text[0]); - else if (text[0] == '-' && text[1] == '\0') - n->ndup.dupfd = -1; - else { - - if (err) - synerror("Bad fd number"); - else - n->ndup.vname = makename(); - } -} - - -STATIC void -parsefname(void) -{ - union node *n = redirnode; - - if (readtoken() != TWORD) - synexpect(-1); - if (n->type == NHERE) { - struct heredoc *here = heredoc; - struct heredoc *p; - int i; - - if (quoteflag == 0) - n->type = NXHERE; - TRACE(("Here document %d\n", n->type)); - if (here->striptabs) { - while (*wordtext == '\t') - wordtext++; - } - if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN) - synerror("Illegal eof marker for << redirection"); - rmescapes(wordtext); - here->eofmark = wordtext; - here->next = NULL; - if (heredoclist == NULL) - heredoclist = here; - else { - for (p = heredoclist ; p->next ; p = p->next); - p->next = here; - } - } else if (n->type == NTOFD || n->type == NFROMFD) { - fixredir(n, wordtext, 0); - } else { - n->nfile.fname = makename(); - } -} - - -/* - * Input any here documents. - */ - -STATIC void -parseheredoc(void) -{ - struct heredoc *here; - union node *n; - - while (heredoclist) { - here = heredoclist; - heredoclist = here->next; - if (needprompt) { - setprompt(2); - needprompt = 0; - } - readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX, - here->eofmark, here->striptabs); - n = (union node *)stalloc(sizeof (struct narg)); - n->narg.type = NARG; - n->narg.next = NULL; - n->narg.text = wordtext; - n->narg.backquote = backquotelist; - here->here->nhere.doc = n; - } -} - -STATIC int -peektoken(void) -{ - int t; - - t = readtoken(); - tokpushback++; - return (t); -} - -STATIC int -readtoken(void) -{ - int t; - int savecheckkwd = checkkwd; -#ifdef DEBUG - int alreadyseen = tokpushback; -#endif - struct alias *ap; - - top: - t = xxreadtoken(); - - if (checkkwd) { - /* - * eat newlines - */ - if (checkkwd == 2) { - checkkwd = 0; - while (t == TNL) { - parseheredoc(); - t = xxreadtoken(); - } - } else - checkkwd = 0; - /* - * check for keywords and aliases - */ - if (t == TWORD && !quoteflag) - { - const char *const *pp; - - for (pp = parsekwd; *pp; pp++) { - if (**pp == *wordtext && equal(*pp, wordtext)) - { - lasttoken = t = pp - - parsekwd + KWDOFFSET; - TRACE(("keyword %s recognized\n", tokname[t])); - goto out; - } - } - if(!noalias && - (ap = lookupalias(wordtext, 1)) != NULL) { - pushstring(ap->val, strlen(ap->val), ap); - checkkwd = savecheckkwd; - goto top; - } - } -out: - checkkwd = (t == TNOT) ? savecheckkwd : 0; - } -#ifdef DEBUG - if (!alreadyseen) - TRACE(("token %s %s\n", tokname[t], t == TWORD ? wordtext : "")); - else - TRACE(("reread token %s %s\n", tokname[t], t == TWORD ? wordtext : "")); -#endif - return (t); -} - - -/* - * Read the next input token. - * If the token is a word, we set backquotelist to the list of cmds in - * backquotes. We set quoteflag to true if any part of the word was - * quoted. - * If the token is TREDIR, then we set redirnode to a structure containing - * the redirection. - * In all cases, the variable startlinno is set to the number of the line - * on which the token starts. - * - * [Change comment: here documents and internal procedures] - * [Readtoken shouldn't have any arguments. Perhaps we should make the - * word parsing code into a separate routine. In this case, readtoken - * doesn't need to have any internal procedures, but parseword does. - * We could also make parseoperator in essence the main routine, and - * have parseword (readtoken1?) handle both words and redirection.] - */ - -#define RETURN(token) return lasttoken = token - -STATIC int -xxreadtoken(void) -{ - int c; - - if (tokpushback) { - tokpushback = 0; - return lasttoken; - } - if (needprompt) { - setprompt(2); - needprompt = 0; - } - startlinno = plinno; - for (;;) { /* until token or start of word found */ - c = pgetc_macro(); - if (c == ' ' || c == '\t') - continue; /* quick check for white space first */ - switch (c) { - case ' ': case '\t': - continue; - case '#': - while ((c = pgetc()) != '\n' && c != PEOF); - pungetc(); - continue; - case '\\': - if (pgetc() == '\n') { - startlinno = ++plinno; - if (doprompt) - setprompt(2); - else - setprompt(0); - continue; - } - pungetc(); - goto breakloop; - case '\n': - plinno++; - needprompt = doprompt; - RETURN(TNL); - case PEOF: - RETURN(TEOF); - case '&': - if (pgetc() == '&') - RETURN(TAND); - pungetc(); - RETURN(TBACKGND); - case '|': - if (pgetc() == '|') - RETURN(TOR); - pungetc(); - RETURN(TPIPE); - case ';': - if (pgetc() == ';') - RETURN(TENDCASE); - pungetc(); - RETURN(TSEMI); - case '(': - RETURN(TLP); - case ')': - RETURN(TRP); - default: - goto breakloop; - } - } -breakloop: - return readtoken1(c, BASESYNTAX, (char *)NULL, 0); -#undef RETURN -} - - - -/* - * If eofmark is NULL, read a word or a redirection symbol. If eofmark - * is not NULL, read a here document. In the latter case, eofmark is the - * word which marks the end of the document and striptabs is true if - * leading tabs should be stripped from the document. The argument firstc - * is the first character of the input token or document. - * - * Because C does not have internal subroutines, I have simulated them - * using goto's to implement the subroutine linkage. The following macros - * will run code that appears at the end of readtoken1. - */ - -#define CHECKEND() {goto checkend; checkend_return:;} -#define PARSEREDIR() {goto parseredir; parseredir_return:;} -#define PARSESUB() {goto parsesub; parsesub_return:;} -#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;} -#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;} -#define PARSEARITH() {goto parsearith; parsearith_return:;} - -/* - * Keep track of nested doublequotes in dblquote and doublequotep. - * We use dblquote for the first 32 levels, and we expand to a malloc'ed - * region for levels above that. Usually we never need to malloc. - * This code assumes that an int is 32 bits. We don't use uint32_t, - * because the rest of the code does not. - */ -#define ISDBLQUOTE() ((varnest < 32) ? (dblquote & (1 << varnest)) : \ - (dblquotep[(varnest / 32) - 1] & (1 << (varnest % 32)))) - -#define SETDBLQUOTE() \ - if (varnest < 32) \ - dblquote |= (1 << varnest); \ - else \ - dblquotep[(varnest / 32) - 1] |= (1 << (varnest % 32)) - -#define CLRDBLQUOTE() \ - if (varnest < 32) \ - dblquote &= ~(1 << varnest); \ - else \ - dblquotep[(varnest / 32) - 1] &= ~(1 << (varnest % 32)) - -STATIC int -readtoken1(int firstc, char const *syntax, char *eofmark, int striptabs) -{ - int c = firstc; - char *out; - int len; - char line[EOFMARKLEN + 1]; - struct nodelist *bqlist; - int quotef; - int *dblquotep = NULL; - size_t maxnest = 32; - int dblquote; - int varnest; /* levels of variables expansion */ - int arinest; /* levels of arithmetic expansion */ - int parenlevel; /* levels of parens in arithmetic */ - int oldstyle; - char const *prevsyntax = NULL; /* syntax before arithmetic */ -#if __GNUC__ - /* Avoid longjmp clobbering */ - (void) &maxnest; - (void) &dblquotep; - (void) &out; - (void) "ef; - (void) &dblquote; - (void) &varnest; - (void) &arinest; - (void) &parenlevel; - (void) &oldstyle; - (void) &prevsyntax; - (void) &syntax; -#endif - - startlinno = plinno; - dblquote = 0; - varnest = 0; - if (syntax == DQSYNTAX) { - SETDBLQUOTE(); - } - quotef = 0; - bqlist = NULL; - arinest = 0; - parenlevel = 0; - - STARTSTACKSTR(out); - loop: { /* for each line, until end of word */ -#if ATTY - if (c == '\034' && doprompt - && attyset() && ! equal(termval(), "emacs")) { - attyline(); - if (syntax == BASESYNTAX) - return readtoken(); - c = pgetc(); - goto loop; - } -#endif - CHECKEND(); /* set c to PEOF if at end of here document */ - for (;;) { /* until end of line or end of word */ - CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */ - switch(syntax[c]) { - case CNL: /* '\n' */ - if (syntax == BASESYNTAX) - goto endword; /* exit outer loop */ - USTPUTC(c, out); - plinno++; - if (doprompt) - setprompt(2); - else - setprompt(0); - c = pgetc(); - goto loop; /* continue outer loop */ - case CWORD: - USTPUTC(c, out); - break; - case CCTL: - if (eofmark == NULL || ISDBLQUOTE()) - USTPUTC(CTLESC, out); - USTPUTC(c, out); - break; - case CBACK: /* backslash */ - c = pgetc(); - if (c == PEOF) { - USTPUTC('\\', out); - pungetc(); - break; - } - if (c == '\n') { - if (doprompt) - setprompt(2); - else - setprompt(0); - break; - } - quotef = 1; - if (ISDBLQUOTE() && c != '\\' && - c != '`' && c != '$' && - (c != '"' || eofmark != NULL)) - USTPUTC('\\', out); - if (SQSYNTAX[c] == CCTL) - USTPUTC(CTLESC, out); - else if (eofmark == NULL) { - USTPUTC(CTLQUOTEMARK, out); - USTPUTC(c, out); - if (varnest != 0) - USTPUTC(CTLQUOTEEND, out); - break; - } - USTPUTC(c, out); - break; - case CSQUOTE: - if (syntax != SQSYNTAX) { - if (eofmark == NULL) - USTPUTC(CTLQUOTEMARK, out); - quotef = 1; - syntax = SQSYNTAX; - break; - } - if (eofmark != NULL && arinest == 0 && - varnest == 0) { - /* Ignore inside quoted here document */ - USTPUTC(c, out); - break; - } - /* End of single quotes... */ - if (arinest) - syntax = ARISYNTAX; - else { - syntax = BASESYNTAX; - if (varnest != 0) - USTPUTC(CTLQUOTEEND, out); - } - break; - case CDQUOTE: - if (eofmark != NULL && arinest == 0 && - varnest == 0) { - /* Ignore inside here document */ - USTPUTC(c, out); - break; - } - quotef = 1; - if (arinest) { - if (ISDBLQUOTE()) { - syntax = ARISYNTAX; - CLRDBLQUOTE(); - } else { - syntax = DQSYNTAX; - SETDBLQUOTE(); - USTPUTC(CTLQUOTEMARK, out); - } - break; - } - if (eofmark != NULL) - break; - if (ISDBLQUOTE()) { - if (varnest != 0) - USTPUTC(CTLQUOTEEND, out); - syntax = BASESYNTAX; - CLRDBLQUOTE(); - } else { - syntax = DQSYNTAX; - SETDBLQUOTE(); - USTPUTC(CTLQUOTEMARK, out); - } - break; - case CVAR: /* '$' */ - PARSESUB(); /* parse substitution */ - break; - case CENDVAR: /* CLOSEBRACE */ - if (varnest > 0 && !ISDBLQUOTE()) { - varnest--; - USTPUTC(CTLENDVAR, out); - } else { - USTPUTC(c, out); - } - break; - case CLP: /* '(' in arithmetic */ - parenlevel++; - USTPUTC(c, out); - break; - case CRP: /* ')' in arithmetic */ - if (parenlevel > 0) { - USTPUTC(c, out); - --parenlevel; - } else { - if (pgetc() == ')') { - if (--arinest == 0) { - USTPUTC(CTLENDARI, out); - syntax = prevsyntax; - if (syntax == DQSYNTAX) - SETDBLQUOTE(); - else - CLRDBLQUOTE(); - } else - USTPUTC(')', out); - } else { - /* - * unbalanced parens - * (don't 2nd guess - no error) - */ - pungetc(); - USTPUTC(')', out); - } - } - break; - case CBQUOTE: /* '`' */ - PARSEBACKQOLD(); - break; - case CEOF: - goto endword; /* exit outer loop */ - default: - if (varnest == 0) - goto endword; /* exit outer loop */ - USTPUTC(c, out); - } - c = pgetc_macro(); - } - } -endword: - if (syntax == ARISYNTAX) - synerror("Missing '))'"); - if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL) - synerror("Unterminated quoted string"); - if (varnest != 0) { - startlinno = plinno; - /* { */ - synerror("Missing '}'"); - } - USTPUTC('\0', out); - len = out - stackblock(); - out = stackblock(); - if (eofmark == NULL) { - if ((c == '>' || c == '<') - && quotef == 0 - && len <= 2 - && (*out == '\0' || is_digit(*out))) { - PARSEREDIR(); - return lasttoken = TREDIR; - } else { - pungetc(); - } - } - quoteflag = quotef; - backquotelist = bqlist; - grabstackblock(len); - wordtext = out; - if (dblquotep != NULL) - ckfree(dblquotep); - return lasttoken = TWORD; -/* end of readtoken routine */ - - - -/* - * Check to see whether we are at the end of the here document. When this - * is called, c is set to the first character of the next input line. If - * we are at the end of the here document, this routine sets the c to PEOF. - */ - -checkend: { - if (eofmark) { - if (striptabs) { - while (c == '\t') - c = pgetc(); - } - if (c == *eofmark) { - if (pfgets(line, sizeof line) != NULL) { - char *p, *q; - - p = line; - for (q = eofmark + 1 ; *q && *p == *q ; p++, q++); - if (*p == '\n' && *q == '\0') { - c = PEOF; - plinno++; - needprompt = doprompt; - } else { - pushstring(line, strlen(line), NULL); - } - } - } - } - goto checkend_return; -} - - -/* - * Parse a redirection operator. The variable "out" points to a string - * specifying the fd to be redirected. The variable "c" contains the - * first character of the redirection operator. - */ - -parseredir: { - char fd = *out; - union node *np; - - np = (union node *)stalloc(sizeof (struct nfile)); - if (c == '>') { - np->nfile.fd = 1; - c = pgetc(); - if (c == '>') - np->type = NAPPEND; - else if (c == '|') - np->type = NCLOBBER; - else if (c == '&') - np->type = NTOFD; - else { - np->type = NTO; - pungetc(); - } - } else { /* c == '<' */ - np->nfile.fd = 0; - switch (c = pgetc()) { - case '<': - if (sizeof (struct nfile) != sizeof (struct nhere)) { - np = (union node *)stalloc(sizeof (struct nhere)); - np->nfile.fd = 0; - } - np->type = NHERE; - heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc)); - heredoc->here = np; - if ((c = pgetc()) == '-') { - heredoc->striptabs = 1; - } else { - heredoc->striptabs = 0; - pungetc(); - } - break; - - case '&': - np->type = NFROMFD; - break; - - case '>': - np->type = NFROMTO; - break; - - default: - np->type = NFROM; - pungetc(); - break; - } - } - if (fd != '\0') - np->nfile.fd = digit_val(fd); - redirnode = np; - goto parseredir_return; -} - - -/* - * Parse a substitution. At this point, we have read the dollar sign - * and nothing else. - */ - -parsesub: { - int subtype; - int typeloc; - int flags; - char *p; - static const char types[] = "}-+?="; - - c = pgetc(); - if (c != '(' && c != OPENBRACE && !is_name(c) && !is_special(c)) { - USTPUTC('$', out); - pungetc(); - } else if (c == '(') { /* $(command) or $((arith)) */ - if (pgetc() == '(') { - PARSEARITH(); - } else { - pungetc(); - PARSEBACKQNEW(); - } - } else { - USTPUTC(CTLVAR, out); - typeloc = out - stackblock(); - USTPUTC(VSNORMAL, out); - subtype = VSNORMAL; - if (c == OPENBRACE) { - c = pgetc(); - if (c == '#') { - if ((c = pgetc()) == CLOSEBRACE) - c = '#'; - else - subtype = VSLENGTH; - } - else - subtype = 0; - } - if (is_name(c)) { - do { - STPUTC(c, out); - c = pgetc(); - } while (is_in_name(c)); - } else if (is_digit(c)) { - do { - USTPUTC(c, out); - c = pgetc(); - } while (is_digit(c)); - } - else if (is_special(c)) { - USTPUTC(c, out); - c = pgetc(); - } - else -badsub: synerror("Bad substitution"); - - STPUTC('=', out); - flags = 0; - if (subtype == 0) { - switch (c) { - case ':': - flags = VSNUL; - c = pgetc(); - /*FALLTHROUGH*/ - default: - p = strchr(types, c); - if (p == NULL) - goto badsub; - subtype = p - types + VSNORMAL; - break; - case '%': - case '#': - { - int cc = c; - subtype = c == '#' ? VSTRIMLEFT : - VSTRIMRIGHT; - c = pgetc(); - if (c == cc) - subtype++; - else - pungetc(); - break; - } - } - } else { - pungetc(); - } - if (ISDBLQUOTE() || arinest) - flags |= VSQUOTE; - *(stackblock() + typeloc) = subtype | flags; - if (subtype != VSNORMAL) { - varnest++; - if (varnest >= maxnest) { - dblquotep = ckrealloc(dblquotep, maxnest / 8); - dblquotep[(maxnest / 32) - 1] = 0; - maxnest += 32; - } - } - } - goto parsesub_return; -} - - -/* - * Called to parse command substitutions. Newstyle is set if the command - * is enclosed inside $(...); nlpp is a pointer to the head of the linked - * list of commands (passed by reference), and savelen is the number of - * characters on the top of the stack which must be preserved. - */ - -parsebackq: { - struct nodelist **nlpp; - int savepbq; - union node *n; - char *volatile str; - struct jmploc jmploc; - struct jmploc *volatile savehandler; - int savelen; - int saveprompt; -#ifdef __GNUC__ - (void) &saveprompt; -#endif - - savepbq = parsebackquote; - if (setjmp(jmploc.loc)) { - if (str) - ckfree(str); - parsebackquote = 0; - handler = savehandler; - longjmp(handler->loc, 1); - } - INTOFF; - str = NULL; - savelen = out - stackblock(); - if (savelen > 0) { - str = ckmalloc(savelen); - memcpy(str, stackblock(), savelen); - } - savehandler = handler; - handler = &jmploc; - INTON; - if (oldstyle) { - /* We must read until the closing backquote, giving special - treatment to some slashes, and then push the string and - reread it as input, interpreting it normally. */ - char *pout; - int pc; - int psavelen; - char *pstr; - - - STARTSTACKSTR(pout); - for (;;) { - if (needprompt) { - setprompt(2); - needprompt = 0; - } - switch (pc = pgetc()) { - case '`': - goto done; - - case '\\': - if ((pc = pgetc()) == '\n') { - plinno++; - if (doprompt) - setprompt(2); - else - setprompt(0); - /* - * If eating a newline, avoid putting - * the newline into the new character - * stream (via the STPUTC after the - * switch). - */ - continue; - } - if (pc != '\\' && pc != '`' && pc != '$' - && (!ISDBLQUOTE() || pc != '"')) - STPUTC('\\', pout); - break; - - case '\n': - plinno++; - needprompt = doprompt; - break; - - case PEOF: - startlinno = plinno; - synerror("EOF in backquote substitution"); - break; - - default: - break; - } - STPUTC(pc, pout); - } -done: - STPUTC('\0', pout); - psavelen = pout - stackblock(); - if (psavelen > 0) { - pstr = grabstackstr(pout); - setinputstring(pstr, 1); - } - } - nlpp = &bqlist; - while (*nlpp) - nlpp = &(*nlpp)->next; - *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist)); - (*nlpp)->next = NULL; - parsebackquote = oldstyle; - - if (oldstyle) { - saveprompt = doprompt; - doprompt = 0; - } - - n = list(0); - - if (oldstyle) - doprompt = saveprompt; - else { - if (readtoken() != TRP) - synexpect(TRP); - } - - (*nlpp)->n = n; - if (oldstyle) { - /* - * Start reading from old file again, ignoring any pushed back - * tokens left from the backquote parsing - */ - popfile(); - tokpushback = 0; - } - while (stackblocksize() <= savelen) - growstackblock(); - STARTSTACKSTR(out); - if (str) { - memcpy(out, str, savelen); - STADJUST(savelen, out); - INTOFF; - ckfree(str); - str = NULL; - INTON; - } - parsebackquote = savepbq; - handler = savehandler; - if (arinest || ISDBLQUOTE()) - USTPUTC(CTLBACKQ | CTLQUOTE, out); - else - USTPUTC(CTLBACKQ, out); - if (oldstyle) - goto parsebackq_oldreturn; - else - goto parsebackq_newreturn; -} - -/* - * Parse an arithmetic expansion (indicate start of one and set state) - */ -parsearith: { - - if (++arinest == 1) { - prevsyntax = syntax; - syntax = ARISYNTAX; - USTPUTC(CTLARI, out); - if (ISDBLQUOTE()) - USTPUTC('"',out); - else - USTPUTC(' ',out); - } else { - /* - * we collapse embedded arithmetic expansion to - * parenthesis, which should be equivalent - */ - USTPUTC('(', out); - } - goto parsearith_return; -} - -} /* end of readtoken */ - - - -#ifdef mkinit -RESET { - tokpushback = 0; - checkkwd = 0; -} -#endif - -/* - * Returns true if the text contains nothing to expand (no dollar signs - * or backquotes). - */ - -STATIC int -noexpand(char *text) -{ - char *p; - char c; - - p = text; - while ((c = *p++) != '\0') { - if (c == CTLQUOTEMARK) - continue; - if (c == CTLESC) - p++; - else if (BASESYNTAX[(int)c] == CCTL) - return 0; - } - return 1; -} - - -/* - * Return true if the argument is a legal variable name (a letter or - * underscore followed by zero or more letters, underscores, and digits). - */ - -int -goodname(char *name) - { - char *p; - - p = name; - if (! is_name(*p)) - return 0; - while (*++p) { - if (! is_in_name(*p)) - return 0; - } - return 1; -} - - -/* - * Called when an unexpected token is read during the parse. The argument - * is the token that is expected, or -1 if more than one type of token can - * occur at this point. - */ - -STATIC void -synexpect(int token) -{ - char msg[64]; - - if (token >= 0) { - fmtstr(msg, 64, "%s unexpected (expecting %s)", - tokname[lasttoken], tokname[token]); - } else { - fmtstr(msg, 64, "%s unexpected", tokname[lasttoken]); - } - synerror(msg); - /* NOTREACHED */ -} - - -STATIC void -synerror(const char *msg) -{ - if (commandname) - outfmt(&errout, "%s: %d: ", commandname, startlinno); - outfmt(&errout, "Syntax error: %s\n", msg); - error((char *)NULL); - /* NOTREACHED */ -} - -STATIC void -setprompt(int which) -{ - whichprompt = which; - -#ifdef WITH_HISTORY - if (!el) -#endif -#ifdef WITH_LINENOISE - if (! in_interactive_mode() ) -#endif - out2str(getprompt(NULL)); -} - -/* - * called by editline -- any expansions to the prompt - * should be added here. - */ -const char * -getprompt(void *unused) - { - switch (whichprompt) { - case 0: - return ""; - case 1: - return ps1val(); - case 2: - return ps2val(); - default: - return "<internal prompt error>"; - } -} diff --git a/sh/parser.h b/sh/parser.h deleted file mode 100644 index b343c71..0000000 --- a/sh/parser.h +++ /dev/null @@ -1,82 +0,0 @@ -/* $NetBSD: parser.h,v 1.17 2004/06/26 22:09:49 dsl 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 - * Kenneth Almquist. - * - * 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. - * - * @(#)parser.h 8.3 (Berkeley) 5/4/95 - */ - -/* control characters in argument strings */ -#define CTL_FIRST '\201' /* first 'special' character */ -#define CTLESC '\201' /* escape next character */ -#define CTLVAR '\202' /* variable defn */ -#define CTLENDVAR '\203' -#define CTLBACKQ '\204' -#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */ -/* CTLBACKQ | CTLQUOTE == '\205' */ -#define CTLARI '\206' /* arithmetic expression */ -#define CTLENDARI '\207' -#define CTLQUOTEMARK '\210' -#define CTLQUOTEEND '\211' /* only inside ${...} */ -#define CTL_LAST '\211' /* last 'special' character */ - -/* variable substitution byte (follows CTLVAR) */ -#define VSTYPE 0x0f /* type of variable substitution */ -#define VSNUL 0x10 /* colon--treat the empty string as unset */ -#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */ - -/* values of VSTYPE field */ -#define VSNORMAL 0x1 /* normal variable: $var or ${var} */ -#define VSMINUS 0x2 /* ${var-text} */ -#define VSPLUS 0x3 /* ${var+text} */ -#define VSQUESTION 0x4 /* ${var?message} */ -#define VSASSIGN 0x5 /* ${var=text} */ -#define VSTRIMLEFT 0x6 /* ${var#pattern} */ -#define VSTRIMLEFTMAX 0x7 /* ${var##pattern} */ -#define VSTRIMRIGHT 0x8 /* ${var%pattern} */ -#define VSTRIMRIGHTMAX 0x9 /* ${var%%pattern} */ -#define VSLENGTH 0xa /* ${#var} */ - - -/* - * NEOF is returned by parsecmd when it encounters an end of file. It - * must be distinct from NULL, so we use the address of a variable that - * happens to be handy. - */ -extern int tokpushback; -#define NEOF ((union node *)&tokpushback) -extern int whichprompt; /* 1 == PS1, 2 == PS2 */ - - -union node *parsecmd(int); -void fixredir(union node *, const char *, int); -int goodname(char *); -const char *getprompt(void *); diff --git a/sh/redir.c b/sh/redir.c deleted file mode 100644 index 5c4c286..0000000 --- a/sh/redir.c +++ /dev/null @@ -1,389 +0,0 @@ -/* $NetBSD: redir.c,v 1.29 2004/07/08 03:57:33 christos 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 - * Kenneth Almquist. - * - * 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[] = "@(#)redir.c 8.2 (Berkeley) 5/4/95"; -#else -__RCSID("$NetBSD: redir.c,v 1.29 2004/07/08 03:57:33 christos Exp $"); -#endif -#endif /* not lint */ - -#include <sys/types.h> -#include <sys/param.h> /* PIPE_BUF */ -#include <signal.h> -#include <string.h> -#include <fcntl.h> -#include <errno.h> -#include <unistd.h> -#include <stdlib.h> - -/* - * Code for dealing with input/output redirection. - */ - -#include "main.h" -#include "shell.h" -#include "nodes.h" -#include "jobs.h" -#include "options.h" -#include "expand.h" -#include "redir.h" -#include "output.h" -#include "memalloc.h" -#include "error.h" - - -#define EMPTY -2 /* marks an unused slot in redirtab */ -#ifndef PIPE_BUF -# define PIPESIZE 4096 /* amount of buffering in a pipe */ -#else -# define PIPESIZE PIPE_BUF -#endif - -#define signal bsd_signal - -MKINIT -struct redirtab { - struct redirtab *next; - short renamed[10]; -}; - - -MKINIT struct redirtab *redirlist; - -/* - * We keep track of whether or not fd0 has been redirected. This is for - * background commands, where we want to redirect fd0 to /dev/null only - * if it hasn't already been redirected. -*/ -int fd0_redirected = 0; - -STATIC void openredirect(union node *, char[10], int); -STATIC int openhere(union node *); - - -/* - * Process a list of redirection commands. If the REDIR_PUSH flag is set, - * old file descriptors are stashed away so that the redirection can be - * undone by calling popredir. If the REDIR_BACKQ flag is set, then the - * standard output, and the standard error if it becomes a duplicate of - * stdout, is saved in memory. - */ - -void -redirect(union node *redir, int flags) -{ - union node *n; - struct redirtab *sv = NULL; - int i; - int fd; - int try; - char memory[10]; /* file descriptors to write to memory */ - - for (i = 10 ; --i >= 0 ; ) - memory[i] = 0; - memory[1] = flags & REDIR_BACKQ; - if (flags & REDIR_PUSH) { - /* We don't have to worry about REDIR_VFORK here, as - * flags & REDIR_PUSH is never true if REDIR_VFORK is set. - */ - sv = ckmalloc(sizeof (struct redirtab)); - for (i = 0 ; i < 10 ; i++) - sv->renamed[i] = EMPTY; - sv->next = redirlist; - redirlist = sv; - } - for (n = redir ; n ; n = n->nfile.next) { - fd = n->nfile.fd; - try = 0; - if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) && - n->ndup.dupfd == fd) - continue; /* redirect from/to same file descriptor */ - - if ((flags & REDIR_PUSH) && sv->renamed[fd] == EMPTY) { - INTOFF; -again: - if ((i = fcntl(fd, F_DUPFD, 10)) == -1) { - switch (errno) { - case EBADF: - if (!try) { - openredirect(n, memory, flags); - try++; - goto again; - } - /* FALLTHROUGH*/ - default: - INTON; - error("%d: %s", fd, strerror(errno)); - /* NOTREACHED */ - } - } - if (!try) { - sv->renamed[fd] = i; - close(fd); - } - INTON; - } else { - close(fd); - } - if (fd == 0) - fd0_redirected++; - if (!try) - openredirect(n, memory, flags); - } - if (memory[1]) - out1 = &memout; - if (memory[2]) - out2 = &memout; -} - - -STATIC void -openredirect(union node *redir, char memory[10], int flags) -{ - int fd = redir->nfile.fd; - char *fname; - int f; - int oflags = O_WRONLY|O_CREAT|O_TRUNC, eflags; - - /* - * We suppress interrupts so that we won't leave open file - * descriptors around. This may not be such a good idea because - * an open of a device or a fifo can block indefinitely. - */ - INTOFF; - memory[fd] = 0; - switch (redir->nfile.type) { - case NFROM: - fname = redir->nfile.expfname; - if (flags & REDIR_VFORK) - eflags = O_NONBLOCK; - else - eflags = 0; - if ((f = open(fname, O_RDONLY|eflags)) < 0) - goto eopen; - if (eflags) - (void)fcntl(f, F_SETFL, fcntl(f, F_GETFL, 0) & ~eflags); - break; - case NFROMTO: - fname = redir->nfile.expfname; - if ((f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666)) < 0) - goto ecreate; - break; - case NTO: - if (Cflag) - oflags |= O_EXCL; - /* FALLTHROUGH */ - case NCLOBBER: - fname = redir->nfile.expfname; - if ((f = open(fname, oflags, 0666)) < 0) - goto ecreate; - break; - case NAPPEND: - fname = redir->nfile.expfname; - if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0) - goto ecreate; - break; - case NTOFD: - case NFROMFD: - if (redir->ndup.dupfd >= 0) { /* if not ">&-" */ - if (memory[redir->ndup.dupfd]) - memory[fd] = 1; - else - copyfd(redir->ndup.dupfd, fd); - } - INTON; - return; - case NHERE: - case NXHERE: - f = openhere(redir); - break; - default: - abort(); - } - - if (f != fd) { - copyfd(f, fd); - close(f); - } - INTON; - return; -ecreate: - error("cannot create %s: %s", fname, errmsg(errno, E_CREAT)); -eopen: - error("cannot open %s: %s", fname, errmsg(errno, E_OPEN)); -} - - -/* - * Handle here documents. Normally we fork off a process to write the - * data to a pipe. If the document is short, we can stuff the data in - * the pipe without forking. - */ - -STATIC int -openhere(union node *redir) -{ - int pip[2]; - int len = 0; - - if (pipe(pip) < 0) - error("Pipe call failed"); - if (redir->type == NHERE) { - len = strlen(redir->nhere.doc->narg.text); - if (len <= PIPESIZE) { - xwrite(pip[1], redir->nhere.doc->narg.text, len); - goto out; - } - } - if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) { - close(pip[0]); - signal(SIGINT, SIG_IGN); - signal(SIGQUIT, SIG_IGN); - signal(SIGHUP, SIG_IGN); -#ifdef SIGTSTP - signal(SIGTSTP, SIG_IGN); -#endif - signal(SIGPIPE, SIG_DFL); - if (redir->type == NHERE) - xwrite(pip[1], redir->nhere.doc->narg.text, len); - else - expandhere(redir->nhere.doc, pip[1]); - _exit(0); - } -out: - close(pip[1]); - return pip[0]; -} - - - -/* - * Undo the effects of the last redirection. - */ - -void -popredir(void) -{ - struct redirtab *rp = redirlist; - int i; - - for (i = 0 ; i < 10 ; i++) { - if (rp->renamed[i] != EMPTY) { - if (i == 0) - fd0_redirected--; - close(i); - if (rp->renamed[i] >= 0) { - copyfd(rp->renamed[i], i); - close(rp->renamed[i]); - } - } - } - INTOFF; - redirlist = rp->next; - ckfree(rp); - INTON; -} - -/* - * Undo all redirections. Called on error or interrupt. - */ - -#ifdef mkinit - -INCLUDE "redir.h" - -RESET { - while (redirlist) - popredir(); -} - -SHELLPROC { - clearredir(0); -} - -#endif - -/* Return true if fd 0 has already been redirected at least once. */ -int -fd0_redirected_p () { - return fd0_redirected != 0; -} - -/* - * Discard all saved file descriptors. - */ - -void -clearredir(vforked) - int vforked; -{ - struct redirtab *rp; - int i; - - for (rp = redirlist ; rp ; rp = rp->next) { - for (i = 0 ; i < 10 ; i++) { - if (rp->renamed[i] >= 0) { - close(rp->renamed[i]); - } - if (!vforked) - rp->renamed[i] = EMPTY; - } - } -} - - - -/* - * Copy a file descriptor to be >= to. Returns -1 - * if the source file descriptor is closed, EMPTY if there are no unused - * file descriptors left. - */ - -int -copyfd(int from, int to) -{ - int newfd; - - newfd = fcntl(from, F_DUPFD, to); - if (newfd < 0) { - if (errno == EMFILE) - return EMPTY; - else - error("%d: %s", from, strerror(errno)); - } - return newfd; -} diff --git a/sh/redir.h b/sh/redir.h deleted file mode 100644 index c9709e9..0000000 --- a/sh/redir.h +++ /dev/null @@ -1,48 +0,0 @@ -/* $NetBSD: redir.h,v 1.15 2003/08/07 09:05:37 agc 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 - * Kenneth Almquist. - * - * 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. - * - * @(#)redir.h 8.2 (Berkeley) 5/4/95 - */ - -/* flags passed to redirect */ -#define REDIR_PUSH 01 /* save previous values of file descriptors */ -#define REDIR_BACKQ 02 /* save the command output in memory */ -#define REDIR_VFORK 04 /* running under vfork(2), be careful */ - -union node; -void redirect(union node *, int); -void popredir(void); -int fd0_redirected_p(void); -void clearredir(int); -int copyfd(int, int); - diff --git a/sh/sh.1 b/sh/sh.1 deleted file mode 100644 index 3ef55b4..0000000 --- a/sh/sh.1 +++ /dev/null @@ -1,1928 +0,0 @@ -.\" $NetBSD: sh.1,v 1.78 2004/06/03 19:54:37 hubertf 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 -.\" Kenneth Almquist. -.\" -.\" 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. -.\" -.\" @(#)sh.1 8.6 (Berkeley) 5/4/95 -.\" -.Dd April 17, 2004 -.Os -.Dt SH 1 -.Sh NAME -.Nm sh -.Nd command interpreter (shell) -.Sh SYNOPSIS -.Nm -.Bk -words -.Op Fl aCefnuvxIimqVEb -.Op Cm +aCefnuvxIimqVEb -.Ek -.Bk -words -.Op Fl o Ar option_name -.Op Cm +o Ar option_name -.Ek -.Bk -words -.Op Ar command_file Oo Ar argument ... Oc -.Ek -.Nm -.Fl c -.Bk -words -.Op Fl aCefnuvxIimqVEb -.Op Cm +aCefnuvxIimqVEb -.Ek -.Bk -words -.Op Fl o Ar option_name -.Op Cm +o Ar option_name -.Ek -.Bk -words -.Ar command_string -.Op Ar command_name Oo Ar argument ... Oc -.Ek -.Nm -.Fl s -.Bk -words -.Op Fl aCefnuvxIimqVEb -.Op Cm +aCefnuvxIimqVEb -.Ek -.Bk -words -.Op Fl o Ar option_name -.Op Cm +o Ar option_name -.Ek -.Bk -words -.Op Ar argument ... -.Ek -.Sh DESCRIPTION -.Nm -is the standard command interpreter for the system. -The current version of -.Nm -is in the process of being changed to conform with the -.Tn POSIX -1003.2 and 1003.2a specifications for the shell. -This version has many -features which make it appear similar in some respects to the Korn shell, -but it is not a Korn shell clone (see -.Xr ksh 1 ) . -Only features designated by -.Tn POSIX , -plus a few Berkeley extensions, are being incorporated into this shell. -.\" We expect -.\" .Tn POSIX -.\" conformance by the time 4.4 BSD is released. -This man page is not intended -to be a tutorial or a complete specification of the shell. -.Ss Overview -The shell is a command that reads lines from either a file or the -terminal, interprets them, and generally executes other commands. -It is the program that is running when a user logs into the system -(although a user can select a different shell with the -.Xr chsh 1 -command). -The shell implements a language that has flow control -constructs, a macro facility that provides a variety of features in -addition to data storage, along with built in history and line editing -capabilities. -It incorporates many features to aid interactive use and -has the advantage that the interpretative language is common to both -interactive and non-interactive use (shell scripts). -That is, commands -can be typed directly to the running shell or can be put into a file and -the file can be executed directly by the shell. -.Ss Invocation -If no args are present and if the standard input of the shell -is connected to a terminal (or if the -.Fl i -flag is set), -and the -.Fl c -option is not present, the shell is considered an interactive shell. -An interactive shell generally prompts before each command and handles -programming and command errors differently (as described below). -When first starting, -the shell inspects argument 0, and if it begins with a dash -.Sq - , -the shell is also considered -a login shell. -This is normally done automatically by the system -when the user first logs in. -A login shell first reads commands -from the files -.Pa /etc/profile -and -.Pa .profile -if they exist. -If the environment variable -.Ev ENV -is set on entry to a shell, or is set in the -.Pa .profile -of a login shell, the shell next reads -commands from the file named in -.Ev ENV . -Therefore, a user should place commands that are to be executed only at -login time in the -.Pa .profile -file, and commands that are executed for every shell inside the -.Ev ENV -file. -To set the -.Ev ENV -variable to some file, place the following line in your -.Pa .profile -of your home directory -.Pp -.Dl ENV=$HOME/.shinit; export ENV -.Pp -substituting for -.Dq .shinit -any filename you wish. -Since the -.Ev ENV -file is read for every invocation of the shell, including shell scripts -and non-interactive shells, the following paradigm is useful for -restricting commands in the -.Ev ENV -file to interactive invocations. -Place commands within the -.Dq case -and -.Dq esac -below (these commands are described later): -.Pp -.Bl -item -compact -offset indent -.It -.Li case $- in *i*) -.Bl -item -compact -offset indent -.It -.Li # commands for interactive use only -.It -.Li ... -.El -.It -.Li esac -.El -.Pp -If command line arguments besides the options have been specified, then -the shell treats the first argument as the name of a file from which to -read commands (a shell script), and the remaining arguments are set as the -positional parameters of the shell ($1, $2, etc). -Otherwise, the shell -reads commands from its standard input. -.Ss Argument List Processing -All of the single letter options have a corresponding name that can be -used as an argument to the -.Fl o -option. -The set -.Fl o -name is provided next to the single letter option in -the description below. -Specifying a dash -.Dq - -turns the option on, while using a plus -.Dq + -disables the option. -The following options can be set from the command line or -with the -.Ic set -builtin (described later). -.Bl -tag -width aaaallexportfoo -offset indent -.It Fl a Em allexport -Export all variables assigned to. -.It Fl c -Read commands from the -.Ar command_string -operand instead of from the standard input. -Special parameter 0 will be set from the -.Ar command_name -operand and the positional parameters ($1, $2, etc.) -set from the remaining argument operands. -.It Fl C Em noclobber -Don't overwrite existing files with -.Dq \*[Gt] . -.It Fl e Em errexit -If not interactive, exit immediately if any untested command fails. -The exit status of a command is considered to be -explicitly tested if the command is used to control an -.Ic if , -.Ic elif , -.Ic while , -or -.Ic until ; -or if the command is the left hand operand of an -.Dq \*[Am]\*[Am] -or -.Dq || -operator. -.It Fl f Em noglob -Disable pathname expansion. -.It Fl n Em noexec -If not interactive, read commands but do not execute them. -This is useful for checking the syntax of shell scripts. -.It Fl u Em nounset -Write a message to standard error when attempting to expand a variable -that is not set, and if the shell is not interactive, exit immediately. -.It Fl v Em verbose -The shell writes its input to standard error as it is read. -Useful for debugging. -.It Fl x Em xtrace -Write each command to standard error (preceded by a -.Sq +\ ) -before it is executed. -Useful for debugging. -.It Fl q Em quietprofile -If the -.Fl v -or -.Fl x -options have been set, do not apply them when reading -initialization files, these being -.Pa /etc/profile , -.Pa .profile , -and the file specified by the -.Ev ENV -environment variable. -.It Fl I Em ignoreeof -Ignore EOF's from input when interactive. -.It Fl i Em interactive -Force the shell to behave interactively. -.It Fl m Em monitor -Turn on job control (set automatically when interactive). -.It Fl s Em stdin -Read commands from standard input (set automatically if no file arguments -are present). -This option has no effect when set after the shell has -already started running (i.e. with -.Ic set ) . -.It Fl V Em vi -Enable the built-in -.Xr vi 1 -command line editor (disables -.Fl E -if it has been set). -(See the -.Sx Command Line Editing -section below.) -.It Fl E Em emacs -Enable the built-in emacs style -command line editor (disables -.Fl V -if it has been set). -(See the -.Sx Command Line Editing -section below.) -.It Fl b Em notify -Enable asynchronous notification of background job completion. -(UNIMPLEMENTED for 4.4alpha) -.It "\ \ " Em cdprint -Make an interactive shell always print the new directory name when -changed by the -.Ic cd -command. -.El -.Ss Lexical Structure -The shell reads input in terms of lines from a file and breaks it up into -words at whitespace (blanks and tabs), and at certain sequences of -characters that are special to the shell called -.Dq operators . -There are two types of operators: control operators and redirection -operators (their meaning is discussed later). -Following is a list of operators: -.Bl -ohang -offset indent -.It "Control operators:" -.Dl \*[Am] \*[Am]\*[Am] \&( \&) \&; ;; | || \*[Lt]newline\*[Gt] -.It "Redirection operators:" -.Dl \*[Lt] \*[Gt] \*[Gt]| \*[Lt]\*[Lt] \*[Gt]\*[Gt] \*[Lt]\*[Am] \*[Gt]\*[Am] \*[Lt]\*[Lt]- \*[Lt]\*[Gt] -.El -.Ss Quoting -Quoting is used to remove the special meaning of certain characters or -words to the shell, such as operators, whitespace, or keywords. -There are three types of quoting: matched single quotes, -matched double quotes, and backslash. -.Ss Backslash -A backslash preserves the literal meaning of the following -character, with the exception of -.Aq newline . -A backslash preceding a -.Aq newline -is treated as a line continuation. -.Ss Single Quotes -Enclosing characters in single quotes preserves the literal meaning of all -the characters (except single quotes, making it impossible to put -single-quotes in a single-quoted string). -.Ss Double Quotes -Enclosing characters within double quotes preserves the literal -meaning of all characters except dollarsign -.Pq $ , -backquote -.Pq ` , -and backslash -.Pq \e . -The backslash inside double quotes is historically weird, and serves to -quote only the following characters: -.Dl $ ` \*q \e \*[Lt]newline\*[Gt] . -Otherwise it remains literal. -.Ss Reserved Words -Reserved words are words that have special meaning to the -shell and are recognized at the beginning of a line and -after a control operator. -The following are reserved words: -.Bl -column while while while while while -offset indent -.It ! Ta elif Ta fi Ta while Ta case -.It else Ta for Ta then Ta { Ta } -.It do Ta done Ta until Ta if Ta esac -.El -.Pp -Their meaning is discussed later. -.Ss Aliases -An alias is a name and corresponding value set using the -.Ic alias -builtin command. -Whenever a reserved word may occur (see above), -and after checking for reserved words, the shell -checks the word to see if it matches an alias. -If it does, it replaces it in the input stream with its value. -For example, if there is an alias called -.Dq lf -with the value -.Dq "ls -F" , -then the input: -.Pp -.Dl lf foobar Aq return -.Pp -would become -.Pp -.Dl ls -F foobar Aq return -.Pp -Aliases provide a convenient way for naive users to create shorthands for -commands without having to learn how to create functions with arguments. -They can also be used to create lexically obscure code. -This use is discouraged. -.Ss Commands -The shell interprets the words it reads according to a language, the -specification of which is outside the scope of this man page (refer to the -BNF in the -.Tn POSIX -1003.2 document). -Essentially though, a line is read and if the first -word of the line (or after a control operator) is not a reserved word, -then the shell has recognized a simple command. -Otherwise, a complex -command or some other special construct may have been recognized. -.Ss Simple Commands -If a simple command has been recognized, the shell performs -the following actions: -.Bl -enum -offset indent -.It -Leading words of the form -.Dq name=value -are stripped off and assigned to the environment of the simple command. -Redirection operators and their arguments (as described below) are -stripped off and saved for processing. -.It -The remaining words are expanded as described in -the section called -.Dq Expansions , -and the first remaining word is considered the command name and the -command is located. -The remaining words are considered the arguments of the command. -If no command name resulted, then the -.Dq name=value -variable assignments recognized in item 1 affect the current shell. -.It -Redirections are performed as described in the next section. -.El -.Ss Redirections -Redirections are used to change where a command reads its input or sends -its output. -In general, redirections open, close, or duplicate an -existing reference to a file. -The overall format used for redirection is: -.Pp -.Dl [n] Va redir-op Ar file -.Pp -where -.Va redir-op -is one of the redirection operators mentioned previously. -Following is a list of the possible redirections. -The -.Bq n -is an optional number, as in -.Sq 3 -(not -.Sq Bq 3 ) , -that refers to a file descriptor. -.Bl -tag -width aaabsfiles -offset indent -.It [n] Ns \*[Gt] file -Redirect standard output (or n) to file. -.It [n] Ns \*[Gt]| file -Same, but override the -.Fl C -option. -.It [n] Ns \*[Gt]\*[Gt] file -Append standard output (or n) to file. -.It [n] Ns \*[Lt] file -Redirect standard input (or n) from file. -.It [n1] Ns \*[Lt]\*[Am] Ns n2 -Duplicate standard input (or n1) from file descriptor n2. -.It [n] Ns \*[Lt]\*[Am]- -Close standard input (or n). -.It [n1] Ns \*[Gt]\*[Am] Ns n2 -Duplicate standard output (or n1) to n2. -.It [n] Ns \*[Gt]\*[Am]- -Close standard output (or n). -.It [n] Ns \*[Lt]\*[Gt] file -Open file for reading and writing on standard input (or n). -.El -.Pp -The following redirection is often called a -.Dq here-document . -.Bl -item -offset indent -.It -.Li [n]\*[Lt]\*[Lt] delimiter -.Dl here-doc-text ... -.Li delimiter -.El -.Pp -All the text on successive lines up to the delimiter is saved away and -made available to the command on standard input, or file descriptor n if -it is specified. -If the delimiter as specified on the initial line is -quoted, then the here-doc-text is treated literally, otherwise the text is -subjected to parameter expansion, command substitution, and arithmetic -expansion (as described in the section on -.Dq Expansions ) . -If the operator is -.Dq \*[Lt]\*[Lt]- -instead of -.Dq \*[Lt]\*[Lt] , -then leading tabs in the here-doc-text are stripped. -.Ss Search and Execution -There are three types of commands: shell functions, builtin commands, and -normal programs -- and the command is searched for (by name) in that order. -They each are executed in a different way. -.Pp -When a shell function is executed, all of the shell positional parameters -(except $0, which remains unchanged) are set to the arguments of the shell -function. -The variables which are explicitly placed in the environment of -the command (by placing assignments to them before the function name) are -made local to the function and are set to the values given. -Then the command given in the function definition is executed. -The positional parameters are restored to their original values -when the command completes. -This all occurs within the current shell. -.Pp -Shell builtins are executed internally to the shell, without spawning a -new process. -.Pp -Otherwise, if the command name doesn't match a function or builtin, the -command is searched for as a normal program in the file system (as -described in the next section). -When a normal program is executed, the shell runs the program, -passing the arguments and the environment to the program. -If the program is not a normal executable file (i.e., if it does -not begin with the "magic number" whose -.Tn ASCII -representation is "#!", so -.Xr execve 2 -returns -.Er ENOEXEC -then) the shell will interpret the program in a subshell. -The child shell will reinitialize itself in this case, -so that the effect will be as if a -new shell had been invoked to handle the ad-hoc shell script, except that -the location of hashed commands located in the parent shell will be -remembered by the child. -.Pp -Note that previous versions of this document and the source code itself -misleadingly and sporadically refer to a shell script without a magic -number as a "shell procedure". -.Ss Path Search -When locating a command, the shell first looks to see if it has a shell -function by that name. -Then it looks for a builtin command by that name. -If a builtin command is not found, one of two things happen: -.Bl -enum -.It -Command names containing a slash are simply executed without performing -any searches. -.It -The shell searches each entry in -.Ev PATH -in turn for the command. -The value of the -.Ev PATH -variable should be a series of entries separated by colons. -Each entry consists of a directory name. -The current directory may be indicated -implicitly by an empty directory name, or explicitly by a single period. -.El -.Ss Command Exit Status -Each command has an exit status that can influence the behavior -of other shell commands. -The paradigm is that a command exits -with zero for normal or success, and non-zero for failure, -error, or a false indication. -The man page for each command -should indicate the various exit codes and what they mean. -Additionally, the builtin commands return exit codes, as does -an executed shell function. -.Pp -If a command consists entirely of variable assignments then the -exit status of the command is that of the last command substitution -if any, otherwise 0. -.Ss Complex Commands -Complex commands are combinations of simple commands with control -operators or reserved words, together creating a larger complex command. -More generally, a command is one of the following: -.Bl -bullet -.It -simple command -.It -pipeline -.It -list or compound-list -.It -compound command -.It -function definition -.El -.Pp -Unless otherwise stated, the exit status of a command is that of the last -simple command executed by the command. -.Ss Pipelines -A pipeline is a sequence of one or more commands separated -by the control operator |. -The standard output of all but -the last command is connected to the standard input -of the next command. -The standard output of the last -command is inherited from the shell, as usual. -.Pp -The format for a pipeline is: -.Pp -.Dl [!] command1 [ | command2 ...] -.Pp -The standard output of command1 is connected to the standard input of -command2. -The standard input, standard output, or both of a command is -considered to be assigned by the pipeline before any redirection specified -by redirection operators that are part of the command. -.Pp -If the pipeline is not in the background (discussed later), the shell -waits for all commands to complete. -.Pp -If the reserved word ! does not precede the pipeline, the exit status is -the exit status of the last command specified in the pipeline. -Otherwise, the exit status is the logical NOT of the exit status of the -last command. -That is, if the last command returns zero, the exit status -is 1; if the last command returns greater than zero, the exit status is -zero. -.Pp -Because pipeline assignment of standard input or standard output or both -takes place before redirection, it can be modified by redirection. -For example: -.Pp -.Dl $ command1 2\*[Gt]\*[Am]1 | command2 -.Pp -sends both the standard output and standard error of command1 -to the standard input of command2. -.Pp -A ; or -.Aq newline -terminator causes the preceding AND-OR-list (described -next) to be executed sequentially; a \*[Am] causes asynchronous execution of -the preceding AND-OR-list. -.Pp -Note that unlike some other shells, each process in the pipeline is a -child of the invoking shell (unless it is a shell builtin, in which case -it executes in the current shell -- but any effect it has on the -environment is wiped). -.Ss Background Commands -- \*[Am] -If a command is terminated by the control operator ampersand (\*[Am]), the -shell executes the command asynchronously -- that is, the shell does not -wait for the command to finish before executing the next command. -.Pp -The format for running a command in background is: -.Pp -.Dl command1 \*[Am] [command2 \*[Am] ...] -.Pp -If the shell is not interactive, the standard input of an asynchronous -command is set to -.Pa /dev/null . -.Ss Lists -- Generally Speaking -A list is a sequence of zero or more commands separated by newlines, -semicolons, or ampersands, and optionally terminated by one of these three -characters. -The commands in a list are executed in the order they are written. -If command is followed by an ampersand, the shell starts the -command and immediately proceed onto the next command; otherwise it waits -for the command to terminate before proceeding to the next one. -.Ss Short-Circuit List Operators -.Dq \*[Am]\*[Am] -and -.Dq || -are AND-OR list operators. -.Dq \*[Am]\*[Am] -executes the first command, and then executes the second command if and only -if the exit status of the first command is zero. -.Dq || -is similar, but executes the second command if and only if the exit status -of the first command is nonzero. -.Dq \*[Am]\*[Am] -and -.Dq || -both have the same priority. -Note that these operators are left-associative, so -.Dq true || echo bar && echo baz -writes -.Dq baz -and nothing else. -This is not the way it works in C. -.Ss Flow-Control Constructs -- if, while, for, case -The syntax of the if command is -.Bd -literal -offset indent -if list -then list -[ elif list -then list ] ... -[ else list ] -fi -.Ed -.Pp -The syntax of the while command is -.Bd -literal -offset indent -while list -do list -done -.Ed -.Pp -The two lists are executed repeatedly while the exit status of the -first list is zero. -The until command is similar, but has the word -until in place of while, which causes it to -repeat until the exit status of the first list is zero. -.Pp -The syntax of the for command is -.Bd -literal -offset indent -for variable in word ... -do list -done -.Ed -.Pp -The words are expanded, and then the list is executed repeatedly with the -variable set to each word in turn. -do and done may be replaced with -.Dq { -and -.Dq } . -.Pp -The syntax of the break and continue command is -.Bd -literal -offset indent -break [ num ] -continue [ num ] -.Ed -.Pp -Break terminates the num innermost for or while loops. -Continue continues with the next iteration of the innermost loop. -These are implemented as builtin commands. -.Pp -The syntax of the case command is -.Bd -literal -offset indent -case word in -pattern) list ;; -\&... -esac -.Ed -.Pp -The pattern can actually be one or more patterns (see -.Sx Shell Patterns -described later), separated by -.Dq \*(Ba -characters. -.Ss Grouping Commands Together -Commands may be grouped by writing either -.Pp -.Dl (list) -.Pp -or -.Pp -.Dl { list; } -.Pp -The first of these executes the commands in a subshell. -Builtin commands grouped into a (list) will not affect the current shell. -The second form does not fork another shell so is slightly more efficient. -Grouping commands together this way allows you to redirect -their output as though they were one program: -.Pp -.Bd -literal -offset indent -{ echo -n \*q hello \*q ; echo \*q world" ; } \*[Gt] greeting -.Ed -.Pp -Note that -.Dq } -must follow a control operator (here, -.Dq \&; ) -so that it is recognized as a reserved word and not as another command argument. -.Ss Functions -The syntax of a function definition is -.Pp -.Dl name ( ) command -.Pp -A function definition is an executable statement; when executed it -installs a function named name and returns an exit status of zero. -The command is normally a list enclosed between -.Dq { -and -.Dq } . -.Pp -Variables may be declared to be local to a function by using a local -command. -This should appear as the first statement of a function, and the syntax is -.Pp -.Dl local [ variable | - ] ... -.Pp -Local is implemented as a builtin command. -.Pp -When a variable is made local, it inherits the initial value and exported -and readonly flags from the variable with the same name in the surrounding -scope, if there is one. -Otherwise, the variable is initially unset. -The shell uses dynamic scoping, so that if you make the variable x local to -function f, which then calls function g, references to the variable x made -inside g will refer to the variable x declared inside f, not to the global -variable named x. -.Pp -The only special parameter that can be made local is -.Dq - . -Making -.Dq - -local any shell options that are changed via the set command inside the -function to be restored to their original values when the function -returns. -.Pp -The syntax of the return command is -.Pp -.Dl return [ exitstatus ] -.Pp -It terminates the currently executing function. -Return is implemented as a builtin command. -.Ss Variables and Parameters -The shell maintains a set of parameters. -A parameter denoted by a name is called a variable. -When starting up, the shell turns all the environment -variables into shell variables. -New variables can be set using the form -.Pp -.Dl name=value -.Pp -Variables set by the user must have a name consisting solely of -alphabetics, numerics, and underscores - the first of which must not be -numeric. -A parameter can also be denoted by a number or a special -character as explained below. -.Ss Positional Parameters -A positional parameter is a parameter denoted by a number (n \*[Gt] 0). -The shell sets these initially to the values of its command line arguments -that follow the name of the shell script. -The -.Ic set -builtin can also be used to set or reset them. -.Ss Special Parameters -A special parameter is a parameter denoted by one of the following special -characters. -The value of the parameter is listed next to its character. -.Bl -tag -width thinhyphena -.It * -Expands to the positional parameters, starting from one. -When the -expansion occurs within a double-quoted string it expands to a single -field with the value of each parameter separated by the first character of -the -.Ev IFS -variable, or by a -.Aq space -if -.Ev IFS -is unset. -.It @ -Expands to the positional parameters, starting from one. -When the expansion occurs within double-quotes, each positional -parameter expands as a separate argument. -If there are no positional parameters, the -expansion of @ generates zero arguments, even when @ is -double-quoted. -What this basically means, for example, is -if $1 is -.Dq abc -and $2 is -.Dq def ghi , -then -.Qq $@ -expands to -the two arguments: -.Pp -.Sm off -.Dl \*q abc \*q \ \*q def\ ghi \*q -.Sm on -.It # -Expands to the number of positional parameters. -.It \&? -Expands to the exit status of the most recent pipeline. -.It - (Hyphen.) -Expands to the current option flags (the single-letter -option names concatenated into a string) as specified on -invocation, by the set builtin command, or implicitly -by the shell. -.It $ -Expands to the process ID of the invoked shell. -A subshell retains the same value of $ as its parent. -.It \&! -Expands to the process ID of the most recent background -command executed from the current shell. -For a pipeline, the process ID is that of the last command in the pipeline. -.It 0 (Zero.) -Expands to the name of the shell or shell script. -.El -.Ss Word Expansions -This clause describes the various expansions that are performed on words. -Not all expansions are performed on every word, as explained later. -.Pp -Tilde expansions, parameter expansions, command substitutions, arithmetic -expansions, and quote removals that occur within a single word expand to a -single field. -It is only field splitting or pathname expansion that can -create multiple fields from a single word. -The single exception to this -rule is the expansion of the special parameter @ within double-quotes, as -was described above. -.Pp -The order of word expansion is: -.Bl -enum -.It -Tilde Expansion, Parameter Expansion, Command Substitution, -Arithmetic Expansion (these all occur at the same time). -.It -Field Splitting is performed on fields -generated by step (1) unless the -.Ev IFS -variable is null. -.It -Pathname Expansion (unless set -.Fl f -is in effect). -.It -Quote Removal. -.El -.Pp -The $ character is used to introduce parameter expansion, command -substitution, or arithmetic evaluation. -.Ss Tilde Expansion (substituting a user's home directory) -A word beginning with an unquoted tilde character (~) is -subjected to tilde expansion. -All the characters up to -a slash (/) or the end of the word are treated as a username -and are replaced with the user's home directory. -If the username is missing (as in -.Pa ~/foobar ) , -the tilde is replaced with the value of the -.Va HOME -variable (the current user's home directory). -.Ss Parameter Expansion -The format for parameter expansion is as follows: -.Pp -.Dl ${expression} -.Pp -where expression consists of all characters until the matching -.Dq } . -Any -.Dq } -escaped by a backslash or within a quoted string, and characters in -embedded arithmetic expansions, command substitutions, and variable -expansions, are not examined in determining the matching -.Dq } . -.Pp -The simplest form for parameter expansion is: -.Pp -.Dl ${parameter} -.Pp -The value, if any, of parameter is substituted. -.Pp -The parameter name or symbol can be enclosed in braces, which are -optional except for positional parameters with more than one digit or -when parameter is followed by a character that could be interpreted as -part of the name. -If a parameter expansion occurs inside double-quotes: -.Bl -enum -.It -Pathname expansion is not performed on the results of the expansion. -.It -Field splitting is not performed on the results of the -expansion, with the exception of @. -.El -.Pp -In addition, a parameter expansion can be modified by using one of the -following formats. -.Bl -tag -width aaparameterwordaaaaa -.It ${parameter:-word} -Use Default Values. -If parameter is unset or null, the expansion of word -is substituted; otherwise, the value of parameter is substituted. -.It ${parameter:=word} -Assign Default Values. -If parameter is unset or null, the expansion of -word is assigned to parameter. -In all cases, the final value of parameter is substituted. -Only variables, not positional parameters or special -parameters, can be assigned in this way. -.It ${parameter:?[word]} -Indicate Error if Null or Unset. -If parameter is unset or null, the -expansion of word (or a message indicating it is unset if word is omitted) -is written to standard error and the shell exits with a nonzero exit status. -Otherwise, the value of parameter is substituted. -An interactive shell need not exit. -.It ${parameter:+word} -Use Alternative Value. -If parameter is unset or null, null is -substituted; otherwise, the expansion of word is substituted. -.El -.Pp -In the parameter expansions shown previously, use of the colon in the -format results in a test for a parameter that is unset or null; omission -of the colon results in a test for a parameter that is only unset. -.Bl -tag -width aaparameterwordaaaaa -.It ${#parameter} -String Length. -The length in characters of the value of parameter. -.El -.Pp -The following four varieties of parameter expansion provide for substring -processing. -In each case, pattern matching notation (see -.Sx Shell Patterns ) , -rather than regular expression notation, is used to evaluate the patterns. -If parameter is * or @, the result of the expansion is unspecified. -Enclosing the full parameter expansion string in double-quotes does not -cause the following four varieties of pattern characters to be quoted, -whereas quoting characters within the braces has this effect. -.Bl -tag -width aaparameterwordaaaaa -.It ${parameter%word} -Remove Smallest Suffix Pattern. -The word is expanded to produce a pattern. -The parameter expansion then results in parameter, with the -smallest portion of the suffix matched by the pattern deleted. -.It ${parameter%%word} -Remove Largest Suffix Pattern. -The word is expanded to produce a pattern. -The parameter expansion then results in parameter, with the largest -portion of the suffix matched by the pattern deleted. -.It ${parameter#word} -Remove Smallest Prefix Pattern. -The word is expanded to produce a pattern. -The parameter expansion then results in parameter, with the -smallest portion of the prefix matched by the pattern deleted. -.It ${parameter##word} -Remove Largest Prefix Pattern. -The word is expanded to produce a pattern. -The parameter expansion then results in parameter, with the largest -portion of the prefix matched by the pattern deleted. -.El -.Ss Command Substitution -Command substitution allows the output of a command to be substituted in -place of the command name itself. -Command substitution occurs when the command is enclosed as follows: -.Pp -.Dl $(command) -.Pp -or -.Po -.Dq backquoted -version -.Pc : -.Pp -.Dl `command` -.Pp -The shell expands the command substitution by executing command in a -subshell environment and replacing the command substitution with the -standard output of the command, removing sequences of one or more -.Ao newline Ac Ns s -at the end of the substitution. -(Embedded -.Ao newline Ac Ns s -before -the end of the output are not removed; however, during field splitting, -they may be translated into -.Ao space Ac Ns s , -depending on the value of -.Ev IFS -and quoting that is in effect.) -.Ss Arithmetic Expansion -Arithmetic expansion provides a mechanism for evaluating an arithmetic -expression and substituting its value. -The format for arithmetic expansion is as follows: -.Pp -.Dl $((expression)) -.Pp -The expression is treated as if it were in double-quotes, except -that a double-quote inside the expression is not treated specially. -The shell expands all tokens in the expression for parameter expansion, -command substitution, and quote removal. -.Pp -Next, the shell treats this as an arithmetic expression and -substitutes the value of the expression. -.Ss White Space Splitting (Field Splitting) -After parameter expansion, command substitution, and -arithmetic expansion the shell scans the results of -expansions and substitutions that did not occur in double-quotes for -field splitting and multiple fields can result. -.Pp -The shell treats each character of the -.Ev IFS -as a delimiter and use the delimiters to split the results of parameter -expansion and command substitution into fields. -.Ss Pathname Expansion (File Name Generation) -Unless the -.Fl f -flag is set, file name generation is performed after word splitting is -complete. -Each word is viewed as a series of patterns, separated by slashes. -The process of expansion replaces the word with the names of all -existing files whose names can be formed by replacing each pattern with a -string that matches the specified pattern. -There are two restrictions on -this: first, a pattern cannot match a string containing a slash, and -second, a pattern cannot match a string starting with a period unless the -first character of the pattern is a period. -The next section describes the -patterns used for both Pathname Expansion and the -.Ic case -command. -.Ss Shell Patterns -A pattern consists of normal characters, which match themselves, -and meta-characters. -The meta-characters are -.Dq \&! , -.Dq * , -.Dq \&? , -and -.Dq \&[ . -These characters lose their special meanings if they are quoted. -When command or variable substitution is performed -and the dollar sign or back quotes are not double quoted, -the value of the variable or the output of -the command is scanned for these characters and they are turned into -meta-characters. -.Pp -An asterisk -.Pq Dq * -matches any string of characters. -A question mark matches any single character. -A left bracket -.Pq Dq \&[ -introduces a character class. -The end of the character class is indicated by a -.Pq Dq \&] ; -if the -.Dq \&] -is missing then the -.Dq \&[ -matches a -.Dq \&[ -rather than introducing a character class. -A character class matches any of the characters between the square brackets. -A range of characters may be specified using a minus sign. -The character class may be complemented -by making an exclamation point the first character of the character class. -.Pp -To include a -.Dq \&] -in a character class, make it the first character listed (after the -.Dq \&! , -if any). -To include a minus sign, make it the first or last character listed. -.Ss Builtins -This section lists the builtin commands which are builtin because they -need to perform some operation that can't be performed by a separate -process. -In addition to these, there are several other commands that may -be builtin for efficiency (e.g. -.Xr printf 1 , -.Xr echo 1 , -.Xr test 1 , -etc). -.Bl -tag -width 5n -.It : -A null command that returns a 0 (true) exit value. -.It \&. file -The commands in the specified file are read and executed by the shell. -.It alias Op Ar name Ns Op Ar "=string ..." -If -.Ar name=string -is specified, the shell defines the alias -.Ar name -with value -.Ar string . -If just -.Ar name -is specified, the value of the alias -.Ar name -is printed. -With no arguments, the -.Ic alias -builtin prints the -names and values of all defined aliases (see -.Ic unalias ) . -.It bg [ Ar job ] ... -Continue the specified jobs (or the current job if no -jobs are given) in the background. -.It Xo command -.Op Fl p -.Op Fl v -.Op Fl V -.Ar command -.Op Ar arg ... -.Xc -Execute the specified command but ignore shell functions when searching -for it. -(This is useful when you -have a shell function with the same name as a builtin command.) -.Bl -tag -width 5n -.It Fl p -search for command using a -.Ev PATH -that guarantees to find all the standard utilities. -.It Fl V -Do not execute the command but -search for the command and print the resolution of the -command search. -This is the same as the type builtin. -.It Fl v -Do not execute the command but -search for the command and print the absolute pathname -of utilities, the name for builtins or the expansion of aliases. -.El -.It cd Op Ar directory Op Ar replace -Switch to the specified directory (default -.Ev $HOME ) . -If -.Ar replace -is specified, then the new directory name is generated by replacing -the first occurrence of -.Ar directory -in the current directory name with -.Ar replace . -Otherwise if an entry for -.Ev CDPATH -appears in the environment of the -.Ic cd -command or the shell variable -.Ev CDPATH -is set and the directory name does not begin with a slash, then the -directories listed in -.Ev CDPATH -will be searched for the specified directory. -The format of -.Ev CDPATH -is the same as that of -.Ev PATH . -In an interactive shell, the -.Ic cd -command will print out the name of the -directory that it actually switched to if this is different from the name -that the user gave. -These may be different either because the -.Ev CDPATH -mechanism was used or because a symbolic link was crossed. -.It eval Ar string ... -Concatenate all the arguments with spaces. -Then re-parse and execute the command. -.It exec Op Ar command arg ... -Unless command is omitted, the shell process is replaced with the -specified program (which must be a real program, not a shell builtin or -function). -Any redirections on the -.Ic exec -command are marked as permanent, so that they are not undone when the -.Ic exec -command finishes. -.It exit Op Ar exitstatus -Terminate the shell process. -If -.Ar exitstatus -is given it is used as the exit status of the shell; otherwise the -exit status of the preceding command is used. -.It export Ar name ... -.It export Fl p -The specified names are exported so that they will appear in the -environment of subsequent commands. -The only way to un-export a variable is to unset it. -The shell allows the value of a variable to be set at the -same time it is exported by writing -.Pp -.Dl export name=value -.Pp -With no arguments the export command lists the names of all exported variables. -With the -.Fl p -option specified the output will be formatted suitably for non-interactive use. -.It Xo fc Op Fl e Ar editor -.Op Ar first Op Ar last -.Xc -.It Xo fc Fl l -.Op Fl nr -.Op Ar first Op Ar last -.Xc -.It Xo fc Fl s Op Ar old=new -.Op Ar first -.Xc -The -.Ic fc -builtin lists, or edits and re-executes, commands previously entered -to an interactive shell. -.Bl -tag -width 5n -.It Fl e No editor -Use the editor named by editor to edit the commands. -The editor string is a command name, subject to search via the -.Ev PATH -variable. -The value in the -.Ev FCEDIT -variable is used as a default when -.Fl e -is not specified. -If -.Ev FCEDIT -is null or unset, the value of the -.Ev EDITOR -variable is used. -If -.Ev EDITOR -is null or unset, -.Xr ed 1 -is used as the editor. -.It Fl l No (ell) -List the commands rather than invoking an editor on them. -The commands are written in the sequence indicated by -the first and last operands, as affected by -.Fl r , -with each command preceded by the command number. -.It Fl n -Suppress command numbers when listing with -l. -.It Fl r -Reverse the order of the commands listed (with -.Fl l ) -or edited (with neither -.Fl l -nor -.Fl s ) . -.It Fl s -Re-execute the command without invoking an editor. -.It first -.It last -Select the commands to list or edit. -The number of previous commands that -can be accessed are determined by the value of the -.Ev HISTSIZE -variable. -The value of first or last or both are one of the following: -.Bl -tag -width 5n -.It [+]number -A positive number representing a command number; command numbers can be -displayed with the -.Fl l -option. -.It Fl number -A negative decimal number representing the command that was executed -number of commands previously. -For example, \-1 is the immediately previous command. -.El -.It string -A string indicating the most recently entered command that begins with -that string. -If the old=new operand is not also specified with -.Fl s , -the string form of the first operand cannot contain an embedded equal sign. -.El -.Pp -The following environment variables affect the execution of fc: -.Bl -tag -width HISTSIZE -.It Ev FCEDIT -Name of the editor to use. -.It Ev HISTSIZE -The number of previous commands that are accessible. -.El -.It fg Op Ar job -Move the specified job or the current job to the foreground. -.It getopts Ar optstring var -The -.Tn POSIX -.Ic getopts -command, not to be confused with the -.Em Bell Labs --derived -.Xr getopt 1 . -.Pp -The first argument should be a series of letters, each of which may be -optionally followed by a colon to indicate that the option requires an -argument. -The variable specified is set to the parsed option. -.Pp -The -.Ic getopts -command deprecates the older -.Xr getopt 1 -utility due to its handling of arguments containing whitespace. -.Pp -The -.Ic getopts -builtin may be used to obtain options and their arguments -from a list of parameters. -When invoked, -.Ic getopts -places the value of the next option from the option string in the list in -the shell variable specified by -.Va var -and its index in the shell variable -.Ev OPTIND . -When the shell is invoked, -.Ev OPTIND -is initialized to 1. -For each option that requires an argument, the -.Ic getopts -builtin will place it in the shell variable -.Ev OPTARG . -If an option is not allowed for in the -.Va optstring , -then -.Ev OPTARG -will be unset. -.Pp -.Va optstring -is a string of recognized option letters (see -.Xr getopt 3 ) . -If a letter is followed by a colon, the option is expected to have an -argument which may or may not be separated from it by white space. -If an option character is not found where expected, -.Ic getopts -will set the variable -.Va var -to a -.Dq \&? ; -.Ic getopts -will then unset -.Ev OPTARG -and write output to standard error. -By specifying a colon as the first character of -.Va optstring -all errors will be ignored. -.Pp -A nonzero value is returned when the last option is reached. -If there are no remaining arguments, -.Ic getopts -will set -.Va var -to the special option, -.Dq -- , -otherwise, it will set -.Va var -to -.Dq \&? . -.Pp -The following code fragment shows how one might process the arguments -for a command that can take the options -.Op a -and -.Op b , -and the option -.Op c , -which requires an argument. -.Pp -.Bd -literal -offset indent -while getopts abc: f -do - case $f in - a | b) flag=$f;; - c) carg=$OPTARG;; - \\?) echo $USAGE; exit 1;; - esac -done -shift `expr $OPTIND - 1` -.Ed -.Pp -This code will accept any of the following as equivalent: -.Pp -.Bd -literal -offset indent -cmd \-acarg file file -cmd \-a \-c arg file file -cmd \-carg -a file file -cmd \-a \-carg \-\- file file -.Ed -.It hash Fl rv Ar command ... -The shell maintains a hash table which remembers the -locations of commands. -With no arguments whatsoever, -the -.Ic hash -command prints out the contents of this table. -Entries which have not been looked at since the last -.Ic cd -command are marked with an asterisk; it is possible for these entries -to be invalid. -.Pp -With arguments, the -.Ic hash -command removes the specified commands from the hash table (unless -they are functions) and then locates them. -With the -.Fl v -option, hash prints the locations of the commands as it finds them. -The -.Fl r -option causes the hash command to delete all the entries in the hash table -except for functions. -.It inputrc Ar file -Read the -.Va file -to set keybindings as defined by -.Xr editrc 5 . -.It jobid Op Ar job -Print the process id's of the processes in the job. -If the -.Ar job -argument is omitted, the current job is used. -.It jobs -This command lists out all the background processes -which are children of the current shell process. -.It pwd Op Fl LP -Print the current directory. -If -.Fl L -is specified the cached value (initially set from -.Ev PWD ) -is checked to see if it refers to the current directory, if it does -the value is printed. -Otherwise the current directory name is found using -.Xr getcwd(3) . -The environment variable -.Ev PWD -is set to printed value. -.Pp -The default is -.Ic pwd -.Fl L , -but note that the builtin -.Ic cd -command doesn't currently support -.Fl L -or -.Fl P -and will cache (almost) the absolute path. -If -.Ic cd -is changed, -.Ic pwd -may be changed to default to -.Ic pwd -.Fl P . -.Pp -If the current directory is renamed and replaced by a symlink to the -same directory, or the initial -.Ev PWD -value followed a symbolic link, then the cached value may not -be the absolute path. -.Pp -The builtin command may differ from the program of the same name because -the program will use -.Ev PWD -and the builtin uses a separately cached value. -.It Xo read Op Fl p Ar prompt -.Op Fl r -.Ar variable -.Op Ar ... -.Xc -The prompt is printed if the -.Fl p -option is specified and the standard input is a terminal. -Then a line is read from the standard input. -The trailing newline is deleted from the -line and the line is split as described in the section on word splitting -above, and the pieces are assigned to the variables in order. -If there are more pieces than variables, the remaining pieces -(along with the characters in -.Ev IFS -that separated them) are assigned to the last variable. -If there are more variables than pieces, -the remaining variables are assigned the null string. -The -.Ic read -builtin will indicate success unless EOF is encountered on input, in -which case failure is returned. -.Pp -By default, unless the -.Fl r -option is specified, the backslash -.Dq \e -acts as an escape character, causing the following character to be treated -literally. -If a backslash is followed by a newline, the backslash and the -newline will be deleted. -.It readonly Ar name ... -.It readonly Fl p -The specified names are marked as read only, so that they cannot be -subsequently modified or unset. -The shell allows the value of a variable -to be set at the same time it is marked read only by writing -.Pp -.Dl readonly name=value -.Pp -With no arguments the readonly command lists the names of all read only -variables. -With the -.Fl p -option specified the output will be formatted suitably for non-interactive use. -.Pp -.It Xo set -.Oo { -.Fl options | Cm +options | Cm -- } -.Oc Ar arg ... -.Xc -The -.Ic set -command performs three different functions. -.Pp -With no arguments, it lists the values of all shell variables. -.Pp -If options are given, it sets the specified option -flags, or clears them as described in the section called -.Sx Argument List Processing . -.Pp -The third use of the set command is to set the values of the shell's -positional parameters to the specified args. -To change the positional -parameters without changing any options, use -.Dq -- -as the first argument to set. -If no args are present, the set command -will clear all the positional parameters (equivalent to executing -.Dq shift $# . ) -.It setvar Ar variable Ar value -Assigns value to variable. -(In general it is better to write -variable=value rather than using -.Ic setvar . -.Ic setvar -is intended to be used in -functions that assign values to variables whose names are passed as -parameters.) -.It shift Op Ar n -Shift the positional parameters n times. -A -.Ic shift -sets the value of -.Va $1 -to the value of -.Va $2 , -the value of -.Va $2 -to the value of -.Va $3 , -and so on, decreasing -the value of -.Va $# -by one. -If there are zero positional parameters, -.Ic shift -does nothing. -.It Xo trap -.Op Fl l -.Xc -.It Xo trap -.Op Ar action -.Ar signal ... -.Xc -Cause the shell to parse and execute action when any of the specified -signals are received. -The signals are specified by signal number or as the name of the signal. -If -.Ar signal -is -.Li 0 , -the action is executed when the shell exits. -.Ar action -may be null, which cause the specified signals to be ignored. -With -.Ar action -omitted or set to `-' the specified signals are set to their default action. -When the shell forks off a subshell, it resets trapped (but not ignored) -signals to the default action. -The -.Ic trap -command has no effect on signals that were -ignored on entry to the shell. -Issuing -.Ic trap -with option -.Ar -l -will print a list of valid signal names. -.Ic trap -without any arguments cause it to write a list of signals and their -associated action to the standard output in a format that is suitable -as an input to the shell that achieves the same trapping results. -.Pp -Examples: -.Pp -.Dl trap -.Pp -List trapped signals and their corresponding action -.Pp -.Dl trap -l -.Pp -Print a list of valid signals -.Pp -.Dl trap '' INT QUIT tstp 30 -.Pp -Ignore signals INT QUIT TSTP USR1 -.Pp -.Dl trap date INT -.Pp -Print date upon receiving signal INT -.It type Op Ar name ... -Interpret each name as a command and print the resolution of the command -search. -Possible resolutions are: -shell keyword, alias, shell builtin, -command, tracked alias and not found. -For aliases the alias expansion is -printed; for commands and tracked aliases the complete pathname of the -command is printed. -.It ulimit Xo -.Op Fl H \*(Ba Fl S -.Op Fl a \*(Ba Fl tfdscmlpn Op Ar value -.Xc -Inquire about or set the hard or soft limits on processes or set new -limits. -The choice between hard limit (which no process is allowed to -violate, and which may not be raised once it has been lowered) and soft -limit (which causes processes to be signaled but not necessarily killed, -and which may be raised) is made with these flags: -.Bl -tag -width Fl -.It Fl H -set or inquire about hard limits -.It Fl S -set or inquire about soft limits. -If neither -.Fl H -nor -.Fl S -is specified, the soft limit is displayed or both limits are set. -If both are specified, the last one wins. -.El -.Pp -.Bl -tag -width Fl -The limit to be interrogated or set, then, is chosen by specifying -any one of these flags: -.It Fl a -show all the current limits -.It Fl b -show or set the limit on the socket buffer size of a process (in bytes) -.It Fl t -show or set the limit on CPU time (in seconds) -.It Fl f -show or set the limit on the largest file that can be created -(in 512-byte blocks) -.It Fl d -show or set the limit on the data segment size of a process (in kilobytes) -.It Fl s -show or set the limit on the stack size of a process (in kilobytes) -.It Fl c -show or set the limit on the largest core dump size that can be produced -(in 512-byte blocks) -.It Fl m -show or set the limit on the total physical memory that can be -in use by a process (in kilobytes) -.It Fl l -show or set the limit on how much memory a process can lock with -.Xr mlock 2 -(in kilobytes) -.It Fl p -show or set the limit on the number of processes this user can -have at one time -.It Fl n -show or set the limit on the number of files a process can have open at once -.El -.Pp -If none of these is specified, it is the limit on file size that is shown -or set. -If value is specified, the limit is set to that number; otherwise -the current limit is displayed. -.Pp -Limits of an arbitrary process can be displayed or set using the -.Xr sysctl 8 -utility. -.Pp -.It umask Op Ar mask -Set the value of umask (see -.Xr umask 2 ) -to the specified octal value. -If the argument is omitted, the umask value is printed. -.It unalias Xo -.Op Fl a -.Op Ar name -.Xc -If -.Ar name -is specified, the shell removes that alias. -If -.Fl a -is specified, all aliases are removed. -.It unset Ar name ... -The specified variables and functions are unset and unexported. -If a given name corresponds to both a variable and a function, both -the variable and the function are unset. -.It wait Op Ar job -Wait for the specified job to complete and return the exit status of the -last process in the job. -If the argument is omitted, wait for all jobs to -complete and then return an exit status of zero. -.El -.Ss Command Line Editing -When -.Nm -is being used interactively from a terminal, the current command -and the command history (see -.Ic fc -in -.Sx Builtins ) -can be edited using emacs-mode or vi-mode command-line editing. -The command -.Ql set -o emacs -enables emacs-mode editing. -The command -.Ql set -o vi -enables vi-mode editing and places sh into vi insert mode. -(See the -.Sx Argument List Processing -section above.) -.Pp -The vi mode uses commands similar to a subset of those described in the -.Xr vi 1 -man page. -With vi-mode -enabled, sh can be switched between insert mode and command mode. -It's similar to vi: typing -.Aq ESC -will throw you into command VI command mode. -Hitting -.Aq return -while in command mode will pass the line to the shell. -.Pp -The emacs mode uses commands similar to a subset available in -the emacs editor. -With emacs-mode enabled, special keys can be used to modify the text -in the buffer using the control key. -.Pp -.Nm -uses the -.Xr editline 3 -library. -.Sh EXIT STATUS -Errors that are detected by the shell, such as a syntax error, will cause the -shell to exit with a non-zero exit status. -If the shell is not an -interactive shell, the execution of the shell file will be aborted. -Otherwise -the shell will return the exit status of the last command executed, or -if the exit builtin is used with a numeric argument, it will return the -argument. -.Sh ENVIRONMENT -.Bl -tag -width MAILCHECK -.It Ev HOME -Set automatically by -.Xr login 1 -from the user's login directory in the password file -.Pq Xr passwd 5 . -This environment variable also functions as the default argument for the -cd builtin. -.It Ev PATH -The default search path for executables. -See the above section -.Sx Path Search . -.It Ev CDPATH -The search path used with the cd builtin. -.It Ev LANG -The string used to specify localization information that allows users -to work with different culture-specific and language conventions. -See -.Xr nls 7 . -.It Ev MAIL -The name of a mail file, that will be checked for the arrival of new mail. -Overridden by -.Ev MAILPATH . -.It Ev MAILCHECK -The frequency in seconds that the shell checks for the arrival of mail -in the files specified by the -.Ev MAILPATH -or the -.Ev MAIL -file. -If set to 0, the check will occur at each prompt. -.It Ev MAILPATH -A colon -.Dq \&: -separated list of file names, for the shell to check for incoming mail. -This environment setting overrides the -.Ev MAIL -setting. -There is a maximum of 10 mailboxes that can be monitored at once. -.It Ev PS1 -The primary prompt string, which defaults to -.Dq $ \ , -unless you are the superuser, in which case it defaults to -.Dq # \ . -.It Ev PS2 -The secondary prompt string, which defaults to -.Dq \*[Gt] \ . -.It Ev PS4 -Output before each line when execution trace (set -x) is enabled, -defaults to -.Dq + \ . -.It Ev IFS -Input Field Separators. -This is normally set to -.Aq space , -.Aq tab , -and -.Aq newline . -See the -.Sx White Space Splitting -section for more details. -.It Ev TERM -The default terminal setting for the shell. -This is inherited by -children of the shell, and is used in the history editing modes. -.It Ev HISTSIZE -The number of lines in the history buffer for the shell. -.El -.Sh FILES -.Bl -item -width HOMEprofilexxxx -.It -.Pa $HOME/.profile -.It -.Pa /etc/profile -.El -.Sh SEE ALSO -.Xr csh 1 , -.Xr echo 1 , -.Xr getopt 1 , -.Xr ksh 1 , -.Xr login 1 , -.Xr printf 1 , -.Xr test 1 , -.Xr editline 3 , -.Xr getopt 3 , -.\" .Xr profile 4 , -.Xr editrc 5 , -.Xr passwd 5 , -.Xr environ 7 , -.Xr nls 7 , -.Xr sysctl 8 -.Sh HISTORY -A -.Nm -command appeared in -.At v1 . -It was, however, unmaintainable so we wrote this one. -.Sh BUGS -Setuid shell scripts should be avoided at all costs, as they are a -significant security risk. -.Pp -PS1, PS2, and PS4 should be subject to parameter expansion before -being displayed. diff --git a/sh/shell.h b/sh/shell.h deleted file mode 100644 index 94be27a..0000000 --- a/sh/shell.h +++ /dev/null @@ -1,83 +0,0 @@ -/* $NetBSD: shell.h,v 1.17 2003/08/07 09:05:38 agc 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 - * Kenneth Almquist. - * - * 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. - * - * @(#)shell.h 8.2 (Berkeley) 5/4/95 - */ - -/* - * The follow should be set to reflect the type of system you have: - * JOBS -> 1 if you have Berkeley job control, 0 otherwise. - * SHORTNAMES -> 1 if your linker cannot handle long names. - * define BSD if you are running 4.2 BSD or later. - * define SYSV if you are running under System V. - * define DEBUG=1 to compile in debugging ('set -o debug' to turn on) - * define DEBUG=2 to compile in and turn on debugging. - * define DO_SHAREDVFORK to indicate that vfork(2) shares its address - * with its parent. - * - * When debugging is on, debugging info will be written to ./trace and - * a quit signal will generate a core dump. - */ - -#include <sys/param.h> - -#define JOBS 1 -#ifndef BSD -#define BSD 1 -#endif - -#ifndef DO_SHAREDVFORK -#if __NetBSD_Version__ >= 104000000 -#define DO_SHAREDVFORK -#endif -#endif - -typedef void *pointer; -#ifndef NULL -#define NULL (void *)0 -#endif -#define STATIC /* empty */ -#define MKINIT /* empty */ - -#include <sys/cdefs.h> - -extern char nullstr[1]; /* null string */ - - -#ifdef DEBUG -#define TRACE(param) trace param -#define TRACEV(param) tracev param -#else -#define TRACE(param) -#define TRACEV(param) -#endif diff --git a/sh/show.c b/sh/show.c deleted file mode 100644 index e92aa51..0000000 --- a/sh/show.c +++ /dev/null @@ -1,425 +0,0 @@ -/* $NetBSD: show.c,v 1.26 2003/11/14 10:46:13 dsl 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 - * Kenneth Almquist. - * - * 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[] = "@(#)show.c 8.3 (Berkeley) 5/4/95"; -#else -__RCSID("$NetBSD: show.c,v 1.26 2003/11/14 10:46:13 dsl Exp $"); -#endif -#endif /* not lint */ - -#include <stdio.h> -#include <stdarg.h> -#include <stdlib.h> - -#include "shell.h" -#include "parser.h" -#include "nodes.h" -#include "mystring.h" -#include "show.h" -#include "options.h" - - -#ifdef DEBUG -static void shtree(union node *, int, char *, FILE*); -static void shcmd(union node *, FILE *); -static void sharg(union node *, FILE *); -static void indent(int, char *, FILE *); -static void trstring(char *); - - -void -showtree(union node *n) -{ - trputs("showtree called\n"); - shtree(n, 1, NULL, stdout); -} - - -static void -shtree(union node *n, int ind, char *pfx, FILE *fp) -{ - struct nodelist *lp; - const char *s; - - if (n == NULL) - return; - - indent(ind, pfx, fp); - switch(n->type) { - case NSEMI: - s = "; "; - goto binop; - case NAND: - s = " && "; - goto binop; - case NOR: - s = " || "; -binop: - shtree(n->nbinary.ch1, ind, NULL, fp); - /* if (ind < 0) */ - fputs(s, fp); - shtree(n->nbinary.ch2, ind, NULL, fp); - break; - case NCMD: - shcmd(n, fp); - if (ind >= 0) - putc('\n', fp); - break; - case NPIPE: - for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { - shcmd(lp->n, fp); - if (lp->next) - fputs(" | ", fp); - } - if (n->npipe.backgnd) - fputs(" &", fp); - if (ind >= 0) - putc('\n', fp); - break; - default: - fprintf(fp, "<node type %d>", n->type); - if (ind >= 0) - putc('\n', fp); - break; - } -} - - - -static void -shcmd(union node *cmd, FILE *fp) -{ - union node *np; - int first; - const char *s; - int dftfd; - - first = 1; - for (np = cmd->ncmd.args ; np ; np = np->narg.next) { - if (! first) - putchar(' '); - sharg(np, fp); - first = 0; - } - for (np = cmd->ncmd.redirect ; np ; np = np->nfile.next) { - if (! first) - putchar(' '); - switch (np->nfile.type) { - case NTO: s = ">"; dftfd = 1; break; - case NCLOBBER: s = ">|"; dftfd = 1; break; - case NAPPEND: s = ">>"; dftfd = 1; break; - case NTOFD: s = ">&"; dftfd = 1; break; - case NFROM: s = "<"; dftfd = 0; break; - case NFROMFD: s = "<&"; dftfd = 0; break; - case NFROMTO: s = "<>"; dftfd = 0; break; - default: s = "*error*"; dftfd = 0; break; - } - if (np->nfile.fd != dftfd) - fprintf(fp, "%d", np->nfile.fd); - fputs(s, fp); - if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) { - fprintf(fp, "%d", np->ndup.dupfd); - } else { - sharg(np->nfile.fname, fp); - } - first = 0; - } -} - - - -static void -sharg(union node *arg, FILE *fp) -{ - char *p; - struct nodelist *bqlist; - int subtype; - - if (arg->type != NARG) { - printf("<node type %d>\n", arg->type); - abort(); - } - bqlist = arg->narg.backquote; - for (p = arg->narg.text ; *p ; p++) { - switch (*p) { - case CTLESC: - putc(*++p, fp); - break; - case CTLVAR: - putc('$', fp); - putc('{', fp); - subtype = *++p; - if (subtype == VSLENGTH) - putc('#', fp); - - while (*p != '=') - putc(*p++, fp); - - if (subtype & VSNUL) - putc(':', fp); - - switch (subtype & VSTYPE) { - case VSNORMAL: - putc('}', fp); - break; - case VSMINUS: - putc('-', fp); - break; - case VSPLUS: - putc('+', fp); - break; - case VSQUESTION: - putc('?', fp); - break; - case VSASSIGN: - putc('=', fp); - break; - case VSTRIMLEFT: - putc('#', fp); - break; - case VSTRIMLEFTMAX: - putc('#', fp); - putc('#', fp); - break; - case VSTRIMRIGHT: - putc('%', fp); - break; - case VSTRIMRIGHTMAX: - putc('%', fp); - putc('%', fp); - break; - case VSLENGTH: - break; - default: - printf("<subtype %d>", subtype); - } - break; - case CTLENDVAR: - putc('}', fp); - break; - case CTLBACKQ: - case CTLBACKQ|CTLQUOTE: - putc('$', fp); - putc('(', fp); - shtree(bqlist->n, -1, NULL, fp); - putc(')', fp); - break; - default: - putc(*p, fp); - break; - } - } -} - - -static void -indent(int amount, char *pfx, FILE *fp) -{ - int i; - - for (i = 0 ; i < amount ; i++) { - if (pfx && i == amount - 1) - fputs(pfx, fp); - putc('\t', fp); - } -} -#endif - - - -/* - * Debugging stuff. - */ - - -FILE *tracefile; - - -#ifdef DEBUG -void -trputc(int c) -{ - if (debug != 1) - return; - putc(c, tracefile); -} -#endif - -void -trace(const char *fmt, ...) -{ -#ifdef DEBUG - va_list va; - - if (debug != 1) - return; - va_start(va, fmt); - (void) vfprintf(tracefile, fmt, va); - va_end(va); -#endif -} - -void -tracev(const char *fmt, va_list va) -{ -#ifdef DEBUG - if (debug != 1) - return; - (void) vfprintf(tracefile, fmt, va); -#endif -} - - -#ifdef DEBUG -void -trputs(const char *s) -{ - if (debug != 1) - return; - fputs(s, tracefile); -} - - -static void -trstring(char *s) -{ - char *p; - char c; - - if (debug != 1) - return; - putc('"', tracefile); - for (p = s ; *p ; p++) { - switch (*p) { - case '\n': c = 'n'; goto backslash; - case '\t': c = 't'; goto backslash; - case '\r': c = 'r'; goto backslash; - case '"': c = '"'; goto backslash; - case '\\': c = '\\'; goto backslash; - case CTLESC: c = 'e'; goto backslash; - case CTLVAR: c = 'v'; goto backslash; - case CTLVAR+CTLQUOTE: c = 'V'; goto backslash; - case CTLBACKQ: c = 'q'; goto backslash; - case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash; -backslash: putc('\\', tracefile); - putc(c, tracefile); - break; - default: - if (*p >= ' ' && *p <= '~') - putc(*p, tracefile); - else { - putc('\\', tracefile); - putc(*p >> 6 & 03, tracefile); - putc(*p >> 3 & 07, tracefile); - putc(*p & 07, tracefile); - } - break; - } - } - putc('"', tracefile); -} -#endif - - -void -trargs(char **ap) -{ -#ifdef DEBUG - if (debug != 1) - return; - while (*ap) { - trstring(*ap++); - if (*ap) - putc(' ', tracefile); - else - putc('\n', tracefile); - } -#endif -} - - -#ifdef DEBUG -void -opentrace(void) -{ - char s[100]; -#ifdef O_APPEND - int flags; -#endif - - if (debug != 1) { - if (tracefile) - fflush(tracefile); - /* leave open because libedit might be using it */ - return; - } -#ifdef not_this_way - { - char *p; - if ((p = getenv("HOME")) == NULL) { - if (geteuid() == 0) - p = "/"; - else - p = "/tmp"; - } - scopy(p, s); - strcat(s, "/trace"); - } -#else - scopy("./trace", s); -#endif /* not_this_way */ - if (tracefile) { - if (!freopen(s, "a", tracefile)) { - fprintf(stderr, "Can't re-open %s\n", s); - debug = 0; - return; - } - } else { - if ((tracefile = fopen(s, "a")) == NULL) { - fprintf(stderr, "Can't open %s\n", s); - debug = 0; - return; - } - } -#ifdef O_APPEND - if ((flags = fcntl(fileno(tracefile), F_GETFL, 0)) >= 0) - fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND); -#endif - setlinebuf(tracefile); - fputs("\nTracing started.\n", tracefile); -} -#endif /* DEBUG */ diff --git a/sh/show.h b/sh/show.h deleted file mode 100644 index 3152ff2..0000000 --- a/sh/show.h +++ /dev/null @@ -1,45 +0,0 @@ -/* $NetBSD: show.h,v 1.7 2003/08/07 09:05:38 agc Exp $ */ - -/*- - * Copyright (c) 1995 - * 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. - * - * @(#)show.h 1.1 (Berkeley) 5/4/95 - */ - -#include <stdarg.h> - -union node; -void showtree(union node *); -void trace(const char *, ...); -void tracev(const char *, va_list); -void trargs(char **); -#ifdef DEBUG -void trputc(int); -void trputs(const char *); -void opentrace(void); -#endif diff --git a/sh/syntax.c b/sh/syntax.c deleted file mode 100644 index 094f674..0000000 --- a/sh/syntax.c +++ /dev/null @@ -1,102 +0,0 @@ -/* $NetBSD: syntax.c,v 1.1 2004/01/17 17:38:12 dsl Exp $ */ - -#include "shell.h" -#include "syntax.h" -#include "parser.h" -#include <limits.h> - -#if CWORD != 0 -#error initialisation assumes 'CWORD' is zero -#endif - -#define ndx(ch) (ch + 1 - CHAR_MIN) -#define set(ch, val) [ndx(ch)] = val, -#define set_range(s, e, val) [ndx(s) ... ndx(e)] = val, - -/* syntax table used when not in quotes */ -const char basesyntax[257] = { CEOF, - set_range(CTL_FIRST, CTL_LAST, CCTL) - set('\n', CNL) - set('\\', CBACK) - set('\'', CSQUOTE) - set('"', CDQUOTE) - set('`', CBQUOTE) - set('$', CVAR) - set('}', CENDVAR) - set('<', CSPCL) - set('>', CSPCL) - set('(', CSPCL) - set(')', CSPCL) - set(';', CSPCL) - set('&', CSPCL) - set('|', CSPCL) - set(' ', CSPCL) - set('\t', CSPCL) -}; - -/* syntax table used when in double quotes */ -const char dqsyntax[257] = { CEOF, - set_range(CTL_FIRST, CTL_LAST, CCTL) - set('\n', CNL) - set('\\', CBACK) - set('"', CDQUOTE) - set('`', CBQUOTE) - set('$', CVAR) - set('}', CENDVAR) - /* ':/' for tilde expansion, '-' for [a\-x] pattern ranges */ - set('!', CCTL) - set('*', CCTL) - set('?', CCTL) - set('[', CCTL) - set('=', CCTL) - set('~', CCTL) - set(':', CCTL) - set('/', CCTL) - set('-', CCTL) -}; - -/* syntax table used when in single quotes */ -const char sqsyntax[257] = { CEOF, - set_range(CTL_FIRST, CTL_LAST, CCTL) - set('\n', CNL) - set('\'', CSQUOTE) - /* ':/' for tilde expansion, '-' for [a\-x] pattern ranges */ - set('!', CCTL) - set('*', CCTL) - set('?', CCTL) - set('[', CCTL) - set('=', CCTL) - set('~', CCTL) - set(':', CCTL) - set('/', CCTL) - set('-', CCTL) -}; - -/* syntax table used when in arithmetic */ -const char arisyntax[257] = { CEOF, - set_range(CTL_FIRST, CTL_LAST, CCTL) - set('\n', CNL) - set('\\', CBACK) - set('`', CBQUOTE) - set('\'', CSQUOTE) - set('"', CDQUOTE) - set('$', CVAR) - set('}', CENDVAR) - set('(', CLP) - set(')', CRP) -}; - -/* character classification table */ -const char is_type[257] = { 0, - set_range('0', '9', ISDIGIT) - set_range('a', 'z', ISLOWER) - set_range('A', 'Z', ISUPPER) - set('_', ISUNDER) - set('#', ISSPECL) - set('?', ISSPECL) - set('$', ISSPECL) - set('!', ISSPECL) - set('-', ISSPECL) - set('*', ISSPECL) - set('@', ISSPECL) -}; diff --git a/sh/syntax.h b/sh/syntax.h deleted file mode 100644 index 89a32dc..0000000 --- a/sh/syntax.h +++ /dev/null @@ -1,83 +0,0 @@ -/* $NetBSD: syntax.h,v 1.2 2004/01/17 17:38:12 dsl 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 - * Kenneth Almquist. - * - * 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> -#include <ctype.h> - -/* Syntax classes */ -#define CWORD 0 /* character is nothing special */ -#define CNL 1 /* newline character */ -#define CBACK 2 /* a backslash character */ -#define CSQUOTE 3 /* single quote */ -#define CDQUOTE 4 /* double quote */ -#define CBQUOTE 5 /* backwards single quote */ -#define CVAR 6 /* a dollar sign */ -#define CENDVAR 7 /* a '}' character */ -#define CLP 8 /* a left paren in arithmetic */ -#define CRP 9 /* a right paren in arithmetic */ -#define CEOF 10 /* end of file */ -#define CCTL 11 /* like CWORD, except it must be escaped */ -#define CSPCL 12 /* these terminate a word */ - -/* Syntax classes for is_ functions */ -#define ISDIGIT 01 /* a digit */ -#define ISUPPER 02 /* an upper case letter */ -#define ISLOWER 04 /* a lower case letter */ -#define ISUNDER 010 /* an underscore */ -#define ISSPECL 020 /* the name of a special parameter */ - -#define PEOF (CHAR_MIN - 1) -#define SYNBASE (-PEOF) -/* XXX UPEOF is CHAR_MAX, so is a valid 'char' value... */ -#define UPEOF ((char)PEOF) - - -#define BASESYNTAX (basesyntax + SYNBASE) -#define DQSYNTAX (dqsyntax + SYNBASE) -#define SQSYNTAX (sqsyntax + SYNBASE) -#define ARISYNTAX (arisyntax + SYNBASE) - -/* These defines assume that the digits are contiguous */ -#define is_digit(c) ((unsigned)((c) - '0') <= 9) -#define is_alpha(c) (((char)(c)) != UPEOF && ((c) < CTL_FIRST || (c) > CTL_LAST) && isalpha((unsigned char)(c))) -#define is_name(c) (((char)(c)) != UPEOF && ((c) < CTL_FIRST || (c) > CTL_LAST) && ((c) == '_' || isalpha((unsigned char)(c)))) -#define is_in_name(c) (((char)(c)) != UPEOF && ((c) < CTL_FIRST || (c) > CTL_LAST) && ((c) == '_' || isalnum((unsigned char)(c)))) -#define is_special(c) ((is_type+SYNBASE)[c] & (ISSPECL|ISDIGIT)) -#define digit_val(c) ((c) - '0') - -extern const char basesyntax[]; -extern const char dqsyntax[]; -extern const char sqsyntax[]; -extern const char arisyntax[]; -extern const char is_type[]; diff --git a/sh/token.h b/sh/token.h deleted file mode 100644 index c961f01..0000000 --- a/sh/token.h +++ /dev/null @@ -1,112 +0,0 @@ -#define TEOF 0 -#define TNL 1 -#define TSEMI 2 -#define TBACKGND 3 -#define TAND 4 -#define TOR 5 -#define TPIPE 6 -#define TLP 7 -#define TRP 8 -#define TENDCASE 9 -#define TENDBQUOTE 10 -#define TREDIR 11 -#define TWORD 12 -#define TIF 13 -#define TTHEN 14 -#define TELSE 15 -#define TELIF 16 -#define TFI 17 -#define TWHILE 18 -#define TUNTIL 19 -#define TFOR 20 -#define TDO 21 -#define TDONE 22 -#define TBEGIN 23 -#define TEND 24 -#define TCASE 25 -#define TESAC 26 -#define TNOT 27 - -/* Array indicating which tokens mark the end of a list */ -const char tokendlist[] = { - 1, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 1, - 1, - 1, - 0, - 0, - 0, - 1, - 1, - 1, - 1, - 0, - 0, - 0, - 1, - 1, - 0, - 1, - 0, - 1, - 0, -}; - -const char *const tokname[] = { - "end of file", - "newline", - "\";\"", - "\"&\"", - "\"&&\"", - "\"||\"", - "\"|\"", - "\"(\"", - "\")\"", - "\";;\"", - "\"`\"", - "redirection", - "word", - "\"if\"", - "\"then\"", - "\"else\"", - "\"elif\"", - "\"fi\"", - "\"while\"", - "\"until\"", - "\"for\"", - "\"do\"", - "\"done\"", - "\"{\"", - "\"}\"", - "\"case\"", - "\"esac\"", - "\"!\"", -}; - -#define KWDOFFSET 13 - -const char *const parsekwd[] = { - "if", - "then", - "else", - "elif", - "fi", - "while", - "until", - "for", - "do", - "done", - "{", - "}", - "case", - "esac", - "!", - 0 -}; diff --git a/sh/trap.c b/sh/trap.c deleted file mode 100644 index dcd76ac..0000000 --- a/sh/trap.c +++ /dev/null @@ -1,456 +0,0 @@ -/* $NetBSD: trap.c,v 1.31 2005/01/11 19:38:57 christos 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 - * Kenneth Almquist. - * - * 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[] = "@(#)trap.c 8.5 (Berkeley) 6/5/95"; -#else -__RCSID("$NetBSD: trap.c,v 1.31 2005/01/11 19:38:57 christos Exp $"); -#endif -#endif /* not lint */ - -#include <signal.h> -#include <unistd.h> -#include <stdlib.h> - -#include "shell.h" -#include "main.h" -#include "nodes.h" /* for other headers */ -#include "eval.h" -#include "jobs.h" -#include "show.h" -#include "options.h" -#include "syntax.h" -#include "output.h" -#include "memalloc.h" -#include "error.h" -#include "trap.h" -#include "mystring.h" -#include "var.h" - -/* - * Sigmode records the current value of the signal handlers for the various - * modes. A value of zero means that the current handler is not known. - * S_HARD_IGN indicates that the signal was ignored on entry to the shell, - */ - -#define S_DFL 1 /* default signal handling (SIG_DFL) */ -#define S_CATCH 2 /* signal is caught */ -#define S_IGN 3 /* signal is ignored (SIG_IGN) */ -#define S_HARD_IGN 4 /* signal is ignored permenantly */ -#define S_RESET 5 /* temporary - to reset a hard ignored sig */ - - -char *trap[NSIG+1]; /* trap handler commands */ -MKINIT char sigmode[NSIG]; /* current value of signal */ -char gotsig[NSIG]; /* indicates specified signal received */ -int pendingsigs; /* indicates some signal received */ - -static int getsigaction(int, sig_t *); - -/* - * return the signal number described by `p' (as a number or a name) - * or -1 if it isn't one - */ - -static int -signame_to_signum(const char *p) -{ - int i; - - if (is_number(p)) - return number(p); - - if (strcasecmp(p, "exit") == 0 ) - return 0; - - if (strncasecmp(p, "sig", 3) == 0) - p += 3; - - for (i = 0; i < NSIG; ++i) - if (sys_signame[i] && (strcasecmp (p, sys_signame[i]) == 0)) - return i; - return -1; -} - -/* - * Print a list of valid signal names - */ -static void -printsignals(void) -{ - int n; - - out1str("EXIT "); - - for (n = 1; n < NSIG; n++) { - out1fmt("%s", sys_signame[n]); - if ((n == NSIG/2) || n == (NSIG - 1)) - out1str("\n"); - else - out1c(' '); - } -} - -/* - * The trap builtin. - */ - -int -trapcmd(int argc, char **argv) -{ - char *action; - char **ap; - int signo; - - if (argc <= 1) { - for (signo = 0 ; signo <= NSIG ; signo++) - if (trap[signo] != NULL) { - out1fmt("trap -- "); - print_quoted(trap[signo]); - out1fmt(" %s\n", - (signo) ? sys_signame[signo] : "EXIT"); - } - return 0; - } - ap = argv + 1; - - action = NULL; - - if (strcmp(*ap, "--") == 0) - if (*++ap == NULL) - return 0; - - if (signame_to_signum(*ap) == -1) { - if ((*ap)[0] == '-') { - if ((*ap)[1] == '\0') - ap++; - else if ((*ap)[1] == 'l' && (*ap)[2] == '\0') { - printsignals(); - return 0; - } - else - error("bad option %s\n", *ap); - } - else - action = *ap++; - } - - while (*ap) { - if (is_number(*ap)) - signo = number(*ap); - else - signo = signame_to_signum(*ap); - - if (signo < 0 || signo > NSIG) - error("%s: bad trap", *ap); - - INTOFF; - if (action) - action = savestr(action); - - if (trap[signo]) - ckfree(trap[signo]); - - trap[signo] = action; - - if (signo != 0) - setsignal(signo, 0); - INTON; - ap++; - } - return 0; -} - - - -/* - * Clear traps on a fork or vfork. - * Takes one arg vfork, to tell it to not be destructive of - * the parents variables. - */ - -void -clear_traps(int vforked) -{ - char **tp; - - for (tp = trap ; tp <= &trap[NSIG] ; tp++) { - if (*tp && **tp) { /* trap not NULL or SIG_IGN */ - INTOFF; - if (!vforked) { - ckfree(*tp); - *tp = NULL; - } - if (tp != &trap[0]) - setsignal(tp - trap, vforked); - INTON; - } - } -} - - - -/* - * Set the signal handler for the specified signal. The routine figures - * out what it should be set to. - */ - -long -setsignal(int signo, int vforked) -{ - int action; - sig_t sigact = SIG_DFL; - struct sigaction act, oact; - char *t, tsig; - - if ((t = trap[signo]) == NULL) - action = S_DFL; - else if (*t != '\0') - action = S_CATCH; - else - action = S_IGN; - if (rootshell && !vforked && action == S_DFL) { - switch (signo) { - case SIGINT: - if (iflag || minusc || sflag == 0) - action = S_CATCH; - break; - case SIGQUIT: -#ifdef DEBUG - if (debug) - break; -#endif - /* FALLTHROUGH */ - case SIGTERM: - if (iflag) - action = S_IGN; - break; -#if JOBS - case SIGTSTP: - case SIGTTOU: - if (mflag) - action = S_IGN; - break; -#endif - } - } - - t = &sigmode[signo - 1]; - tsig = *t; - if (tsig == 0) { - /* - * current setting unknown - */ - if (!getsigaction(signo, &sigact)) { - /* - * Pretend it worked; maybe we should give a warning - * here, but other shells don't. We don't alter - * sigmode, so that we retry every time. - */ - return 0; - } - if (sigact == SIG_IGN) { - if (mflag && (signo == SIGTSTP || - signo == SIGTTIN || signo == SIGTTOU)) { - tsig = S_IGN; /* don't hard ignore these */ - } else - tsig = S_HARD_IGN; - } else { - tsig = S_RESET; /* force to be set */ - } - } - if (tsig == S_HARD_IGN || tsig == action) - return 0; - switch (action) { - case S_DFL: sigact = SIG_DFL; break; - case S_CATCH: sigact = onsig; break; - case S_IGN: sigact = SIG_IGN; break; - } - if (!vforked) - *t = action; - act.sa_handler = sigact; - sigemptyset(&act.sa_mask); - act.sa_flags = 0; -#ifdef SA_INTERRUPT - act.sa_flags |= SA_INTERRUPT; -#endif - if(sigaction(signo, &act, &oact) < 0) - return (long) SIG_ERR; - return (long) oact.sa_handler; -} - -/* - * Return the current setting for sig w/o changing it. - */ -static int -getsigaction(int signo, sig_t *sigact) -{ - struct sigaction sa; - - if (sigaction(signo, (struct sigaction *)0, &sa) == -1) - return 0; - *sigact = (sig_t) sa.sa_handler; - return 1; -} - -/* - * Ignore a signal. - */ - -void -ignoresig(int signo, int vforked) -{ - if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) - bsd_signal(signo, SIG_IGN); - if (!vforked) - sigmode[signo - 1] = S_HARD_IGN; -} - - -#ifdef mkinit -INCLUDE <signal.h> -INCLUDE "trap.h" - -SHELLPROC { - char *sm; - - clear_traps(0); - for (sm = sigmode ; sm < sigmode + NSIG ; sm++) { - if (*sm == S_IGN) - *sm = S_HARD_IGN; - } -} -#endif - - - -/* - * Signal handler. - */ - -void -onsig(int signo) -{ - bsd_signal(signo, onsig); - if (signo == SIGINT && trap[SIGINT] == NULL) { - onint(); - return; - } - gotsig[signo - 1] = 1; - pendingsigs++; -} - - - -/* - * Called to execute a trap. Perhaps we should avoid entering new trap - * handlers while we are executing a trap handler. - */ - -void -dotrap(void) -{ - int i; - int savestatus; - - for (;;) { - for (i = 1 ; ; i++) { - if (gotsig[i - 1]) - break; - if (i >= NSIG) - goto done; - } - gotsig[i - 1] = 0; - savestatus=exitstatus; - evalstring(trap[i], 0); - exitstatus=savestatus; - } -done: - pendingsigs = 0; -} - - - -/* - * Controls whether the shell is interactive or not. - */ - - -void -setinteractive(int on) -{ - static int is_interactive; - - if (on == is_interactive) - return; - setsignal(SIGINT, 0); - setsignal(SIGQUIT, 0); - setsignal(SIGTERM, 0); - is_interactive = on; -} - - - -/* - * Called to exit the shell. - */ - -void -exitshell(int status) -{ - struct jmploc loc1, loc2; - char *p; - - TRACE(("pid %d, exitshell(%d)\n", getpid(), status)); - if (setjmp(loc1.loc)) { - goto l1; - } - if (setjmp(loc2.loc)) { - goto l2; - } - handler = &loc1; - if ((p = trap[0]) != NULL && *p != '\0') { - trap[0] = NULL; - evalstring(p, 0); - } -l1: handler = &loc2; /* probably unnecessary */ - flushall(); -#if JOBS - setjobctl(0); -#endif -l2: _exit(status); - /* NOTREACHED */ -} diff --git a/sh/trap.h b/sh/trap.h deleted file mode 100644 index 125ef40..0000000 --- a/sh/trap.h +++ /dev/null @@ -1,46 +0,0 @@ -/* $NetBSD: trap.h,v 1.17 2003/08/07 09:05:39 agc 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 - * Kenneth Almquist. - * - * 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. - * - * @(#)trap.h 8.3 (Berkeley) 6/5/95 - */ - -extern int pendingsigs; - -int trapcmd(int, char **); -void clear_traps(int); -long setsignal(int, int); -void ignoresig(int, int); -void onsig(int); -void dotrap(void); -void setinteractive(int); -void exitshell(int) __attribute__((__noreturn__)); diff --git a/sh/var.c b/sh/var.c deleted file mode 100644 index a1f1689..0000000 --- a/sh/var.c +++ /dev/null @@ -1,825 +0,0 @@ -/* $NetBSD: var.c,v 1.36 2004/10/06 10:23:43 enami 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 - * Kenneth Almquist. - * - * 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[] = "@(#)var.c 8.3 (Berkeley) 5/4/95"; -#else -__RCSID("$NetBSD: var.c,v 1.36 2004/10/06 10:23:43 enami Exp $"); -#endif -#endif /* not lint */ - -#include <unistd.h> -#include <stdlib.h> -#include <paths.h> - -/* - * Shell variables. - */ - -#include "shell.h" -#include "output.h" -#include "expand.h" -#include "nodes.h" /* for other headers */ -#include "eval.h" /* defines cmdenviron */ -#include "exec.h" -#include "syntax.h" -#include "options.h" -#include "var.h" -#include "memalloc.h" -#include "error.h" -#include "mystring.h" -#include "parser.h" -#include "show.h" -#ifndef SMALL -#include "myhistedit.h" -#endif - -#ifdef SMALL -#define VTABSIZE 39 -#else -#define VTABSIZE 517 -#endif - - -struct varinit { - struct var *var; - int flags; - const char *text; - void (*func)(const char *); -}; - - -#if ATTY -struct var vatty; -#endif -#ifdef WITH_HISTORY -struct var vhistsize; -struct var vterm; -#endif -struct var vifs; -struct var vmpath; -struct var vpath; -struct var vps1; -struct var vps2; -struct var vps4; -struct var vvers; -struct var voptind; - -const struct varinit varinit[] = { -#if ATTY - { &vatty, VSTRFIXED|VTEXTFIXED|VUNSET, "ATTY=", - NULL }, -#endif -#ifdef WITH_HISTORY - { &vhistsize, VSTRFIXED|VTEXTFIXED|VUNSET, "HISTSIZE=", - sethistsize }, -#endif - { &vifs, VSTRFIXED|VTEXTFIXED, "IFS= \t\n", - NULL }, - { &vmpath, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH=", - NULL }, - { &vpath, VSTRFIXED|VTEXTFIXED, "PATH=" _PATH_DEFPATH, - changepath }, - /* - * vps1 depends on uid - */ - { &vps2, VSTRFIXED|VTEXTFIXED, "PS2=> ", - NULL }, - { &vps4, VSTRFIXED|VTEXTFIXED, "PS4=+ ", - NULL }, -#ifdef WITH_HISTORY - { &vterm, VSTRFIXED|VTEXTFIXED|VUNSET, "TERM=", - setterm }, -#endif - { &voptind, VSTRFIXED|VTEXTFIXED|VNOFUNC, "OPTIND=1", - getoptsreset }, - { NULL, 0, NULL, - NULL } -}; - -struct var *vartab[VTABSIZE]; - -STATIC int strequal(const char *, const char *); -STATIC struct var *find_var(const char *, struct var ***, int *); - -/* - * Initialize the varable symbol tables and import the environment - */ - -#ifdef mkinit -INCLUDE "var.h" -MKINIT char **environ; -INIT { - char **envp; - - initvar(); - for (envp = environ ; *envp ; envp++) { - if (strchr(*envp, '=')) { - setvareq(*envp, VEXPORT|VTEXTFIXED); - } - } -} -#endif - - -/* - * This routine initializes the builtin variables. It is called when the - * shell is initialized and again when a shell procedure is spawned. - */ - -void -initvar(void) -{ - const struct varinit *ip; - struct var *vp; - struct var **vpp; - - for (ip = varinit ; (vp = ip->var) != NULL ; ip++) { - if (find_var(ip->text, &vpp, &vp->name_len) != NULL) - continue; - vp->next = *vpp; - *vpp = vp; - vp->text = strdup(ip->text); - vp->flags = ip->flags; - vp->func = ip->func; - } - /* - * PS1 depends on uid - */ - if (find_var("PS1", &vpp, &vps1.name_len) == NULL) { - vps1.next = *vpp; - *vpp = &vps1; - vps1.text = strdup(geteuid() ? "PS1=$ " : "PS1=# "); - vps1.flags = VSTRFIXED|VTEXTFIXED; - } -} - -/* - * Safe version of setvar, returns 1 on success 0 on failure. - */ - -int -setvarsafe(const char *name, const char *val, int flags) -{ - struct jmploc jmploc; - struct jmploc *volatile savehandler = handler; - int err = 0; -#ifdef __GNUC__ - (void) &err; -#endif - - if (setjmp(jmploc.loc)) - err = 1; - else { - handler = &jmploc; - setvar(name, val, flags); - } - handler = savehandler; - return err; -} - -/* - * Set the value of a variable. The flags argument is ored with the - * flags of the variable. If val is NULL, the variable is unset. - */ - -void -setvar(const char *name, const char *val, int flags) -{ - const char *p; - const char *q; - char *d; - int len; - int namelen; - char *nameeq; - int isbad; - - isbad = 0; - p = name; - if (! is_name(*p)) - isbad = 1; - p++; - for (;;) { - if (! is_in_name(*p)) { - if (*p == '\0' || *p == '=') - break; - isbad = 1; - } - p++; - } - namelen = p - name; - if (isbad) - error("%.*s: bad variable name", namelen, name); - len = namelen + 2; /* 2 is space for '=' and '\0' */ - if (val == NULL) { - flags |= VUNSET; - } else { - len += strlen(val); - } - d = nameeq = ckmalloc(len); - q = name; - while (--namelen >= 0) - *d++ = *q++; - *d++ = '='; - *d = '\0'; - if (val) - scopy(val, d); - setvareq(nameeq, flags); -} - - - -/* - * Same as setvar except that the variable and value are passed in - * the first argument as name=value. Since the first argument will - * be actually stored in the table, it should not be a string that - * will go away. - */ - -void -setvareq(char *s, int flags) -{ - struct var *vp, **vpp; - int nlen; - - if (aflag) - flags |= VEXPORT; - vp = find_var(s, &vpp, &nlen); - if (vp != NULL) { - if (vp->flags & VREADONLY) - error("%.*s: is read only", vp->name_len, s); - if (flags & VNOSET) - return; - INTOFF; - - if (vp->func && (flags & VNOFUNC) == 0) - (*vp->func)(s + vp->name_len + 1); - - if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) - ckfree(vp->text); - - vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET); - vp->flags |= flags & ~VNOFUNC; - vp->text = s; - - INTON; - return; - } - /* not found */ - if (flags & VNOSET) - return; - vp = ckmalloc(sizeof (*vp)); - vp->flags = flags & ~VNOFUNC; - vp->text = s; - vp->name_len = nlen; - vp->next = *vpp; - vp->func = NULL; - *vpp = vp; -} - - - -/* - * Process a linked list of variable assignments. - */ - -void -listsetvar(struct strlist *list, int flags) -{ - struct strlist *lp; - - INTOFF; - for (lp = list ; lp ; lp = lp->next) { - setvareq(savestr(lp->text), flags); - } - INTON; -} - -void -listmklocal(struct strlist *list, int flags) -{ - struct strlist *lp; - - for (lp = list ; lp ; lp = lp->next) - mklocal(lp->text, flags); -} - - -/* - * Find the value of a variable. Returns NULL if not set. - */ - -char * -lookupvar(const char *name) -{ - struct var *v; - - v = find_var(name, NULL, NULL); - if (v == NULL || v->flags & VUNSET) - return NULL; - return v->text + v->name_len + 1; -} - - - -/* - * Search the environment of a builtin command. If the second argument - * is nonzero, return the value of a variable even if it hasn't been - * exported. - */ - -char * -bltinlookup(const char *name, int doall) -{ - struct strlist *sp; - struct var *v; - - for (sp = cmdenviron ; sp ; sp = sp->next) { - if (strequal(sp->text, name)) - return strchr(sp->text, '=') + 1; - } - - v = find_var(name, NULL, NULL); - - if (v == NULL || v->flags & VUNSET || (!doall && !(v->flags & VEXPORT))) - return NULL; - return v->text + v->name_len + 1; -} - - - -/* - * Generate a list of exported variables. This routine is used to construct - * the third argument to execve when executing a program. - */ - -char ** -environment(void) -{ - int nenv; - struct var **vpp; - struct var *vp; - char **env; - char **ep; - - nenv = 0; - for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { - for (vp = *vpp ; vp ; vp = vp->next) - if (vp->flags & VEXPORT) - nenv++; - } - ep = env = stalloc((nenv + 1) * sizeof *env); - for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { - for (vp = *vpp ; vp ; vp = vp->next) - if (vp->flags & VEXPORT) - *ep++ = vp->text; - } - *ep = NULL; - return env; -} - - -/* - * Called when a shell procedure is invoked to clear out nonexported - * variables. It is also necessary to reallocate variables of with - * VSTACK set since these are currently allocated on the stack. - */ - -#ifdef mkinit -void shprocvar(void); - -SHELLPROC { - shprocvar(); -} -#endif - -void -shprocvar(void) -{ - struct var **vpp; - struct var *vp, **prev; - - for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { - for (prev = vpp ; (vp = *prev) != NULL ; ) { - if ((vp->flags & VEXPORT) == 0) { - *prev = vp->next; - if ((vp->flags & VTEXTFIXED) == 0) - ckfree(vp->text); - if ((vp->flags & VSTRFIXED) == 0) - ckfree(vp); - } else { - if (vp->flags & VSTACK) { - vp->text = savestr(vp->text); - vp->flags &=~ VSTACK; - } - prev = &vp->next; - } - } - } - initvar(); -} - - - -/* - * Command to list all variables which are set. Currently this command - * is invoked from the set command when the set command is called without - * any variables. - */ - -void -print_quoted(const char *p) -{ - const char *q; - - if (strcspn(p, "|&;<>()$`\\\"' \t\n*?[]#~=%") == strlen(p)) { - out1fmt("%s", p); - return; - } - while (*p) { - if (*p == '\'') { - out1fmt("\\'"); - p++; - continue; - } - q = index(p, '\''); - if (!q) { - out1fmt("'%s'", p ); - return; - } - out1fmt("'%.*s'", (int)(q - p), p ); - p = q; - } -} - -static int -sort_var(const void *v_v1, const void *v_v2) -{ - const struct var * const *v1 = v_v1; - const struct var * const *v2 = v_v2; - - /* XXX Will anyone notice we include the '=' of the shorter name? */ - return strcmp((*v1)->text, (*v2)->text); -} - -/* - * POSIX requires that 'set' (but not export or readonly) output the - * variables in lexicographic order - by the locale's collating order (sigh). - * Maybe we could keep them in an ordered balanced binary tree - * instead of hashed lists. - * For now just roll 'em through qsort for printing... - */ - -int -showvars(const char *name, int flag, int show_value) -{ - struct var **vpp; - struct var *vp; - const char *p; - - static struct var **list; /* static in case we are interrupted */ - static int list_len; - int count = 0; - - if (!list) { - list_len = 32; - list = ckmalloc(list_len * sizeof *list); - } - - for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { - for (vp = *vpp ; vp ; vp = vp->next) { - if (flag && !(vp->flags & flag)) - continue; - if (vp->flags & VUNSET && !(show_value & 2)) - continue; - if (count >= list_len) { - list = ckrealloc(list, - (list_len << 1) * sizeof *list); - list_len <<= 1; - } - list[count++] = vp; - } - } - - qsort(list, count, sizeof *list, sort_var); - - for (vpp = list; count--; vpp++) { - vp = *vpp; - if (name) - out1fmt("%s ", name); - for (p = vp->text ; *p != '=' ; p++) - out1c(*p); - if (!(vp->flags & VUNSET) && show_value) { - out1fmt("="); - print_quoted(++p); - } - out1c('\n'); - } - return 0; -} - - - -/* - * The export and readonly commands. - */ - -int -exportcmd(int argc, char **argv) -{ - struct var *vp; - char *name; - const char *p; - int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT; - int pflag; - - pflag = nextopt("p") == 'p' ? 3 : 0; - if (argc <= 1 || pflag) { - showvars( pflag ? argv[0] : 0, flag, pflag ); - return 0; - } - - while ((name = *argptr++) != NULL) { - if ((p = strchr(name, '=')) != NULL) { - p++; - } else { - vp = find_var(name, NULL, NULL); - if (vp != NULL) { - vp->flags |= flag; - continue; - } - } - setvar(name, p, flag); - } - return 0; -} - - -/* - * The "local" command. - */ - -int -localcmd(int argc, char **argv) -{ - char *name; - - if (! in_function()) - error("Not in a function"); - while ((name = *argptr++) != NULL) { - mklocal(name, 0); - } - return 0; -} - - -/* - * Make a variable a local variable. When a variable is made local, it's - * value and flags are saved in a localvar structure. The saved values - * will be restored when the shell function returns. We handle the name - * "-" as a special case. - */ - -void -mklocal(const char *name, int flags) -{ - struct localvar *lvp; - struct var **vpp; - struct var *vp; - - INTOFF; - lvp = ckmalloc(sizeof (struct localvar)); - if (name[0] == '-' && name[1] == '\0') { - char *p; - p = ckmalloc(sizeof_optlist); - lvp->text = memcpy(p, optlist, sizeof_optlist); - vp = NULL; - } else { - vp = find_var(name, &vpp, NULL); - if (vp == NULL) { - if (strchr(name, '=')) - setvareq(savestr(name), VSTRFIXED|flags); - else - setvar(name, NULL, VSTRFIXED|flags); - vp = *vpp; /* the new variable */ - lvp->text = NULL; - lvp->flags = VUNSET; - } else { - lvp->text = vp->text; - lvp->flags = vp->flags; - vp->flags |= VSTRFIXED|VTEXTFIXED; - if (name[vp->name_len] == '=') - setvareq(savestr(name), flags); - } - } - lvp->vp = vp; - lvp->next = localvars; - localvars = lvp; - INTON; -} - - -/* - * Called after a function returns. - */ - -void -poplocalvars(void) -{ - struct localvar *lvp; - struct var *vp; - - while ((lvp = localvars) != NULL) { - localvars = lvp->next; - vp = lvp->vp; - TRACE(("poplocalvar %s", vp ? vp->text : "-")); - if (vp == NULL) { /* $- saved */ - memcpy(optlist, lvp->text, sizeof_optlist); - ckfree(lvp->text); - } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) { - (void)unsetvar(vp->text, 0); - } else { - if (vp->func && (vp->flags & VNOFUNC) == 0) - (*vp->func)(lvp->text + vp->name_len + 1); - if ((vp->flags & VTEXTFIXED) == 0) - ckfree(vp->text); - vp->flags = lvp->flags; - vp->text = lvp->text; - } - ckfree(lvp); - } -} - - -int -setvarcmd(int argc, char **argv) -{ - if (argc <= 2) - return unsetcmd(argc, argv); - else if (argc == 3) - setvar(argv[1], argv[2], 0); - else - error("List assignment not implemented"); - return 0; -} - - -/* - * The unset builtin command. We unset the function before we unset the - * variable to allow a function to be unset when there is a readonly variable - * with the same name. - */ - -int -unsetcmd(int argc, char **argv) -{ - char **ap; - int i; - int flg_func = 0; - int flg_var = 0; - int ret = 0; - - while ((i = nextopt("evf")) != '\0') { - if (i == 'f') - flg_func = 1; - else - flg_var = i; - } - if (flg_func == 0 && flg_var == 0) - flg_var = 1; - - for (ap = argptr; *ap ; ap++) { - if (flg_func) - ret |= unsetfunc(*ap); - if (flg_var) - ret |= unsetvar(*ap, flg_var == 'e'); - } - return ret; -} - - -/* - * Unset the specified variable. - */ - -int -unsetvar(const char *s, int unexport) -{ - struct var **vpp; - struct var *vp; - - vp = find_var(s, &vpp, NULL); - if (vp == NULL) - return 1; - - if (vp->flags & VREADONLY) - return (1); - - INTOFF; - if (unexport) { - vp->flags &= ~VEXPORT; - } else { - if (vp->text[vp->name_len + 1] != '\0') - setvar(s, nullstr, 0); - vp->flags &= ~VEXPORT; - vp->flags |= VUNSET; - if ((vp->flags & VSTRFIXED) == 0) { - if ((vp->flags & VTEXTFIXED) == 0) - ckfree(vp->text); - *vpp = vp->next; - ckfree(vp); - } - } - INTON; - return 0; -} - - -/* - * Returns true if the two strings specify the same varable. The first - * variable name is terminated by '='; the second may be terminated by - * either '=' or '\0'. - */ - -STATIC int -strequal(const char *p, const char *q) -{ - while (*p == *q++) { - if (*p++ == '=') - return 1; - } - if (*p == '=' && *(q - 1) == '\0') - return 1; - return 0; -} - -/* - * Search for a variable. - * 'name' may be terminated by '=' or a NUL. - * vppp is set to the pointer to vp, or the list head if vp isn't found - * lenp is set to the number of charactets in 'name' - */ - -STATIC struct var * -find_var(const char *name, struct var ***vppp, int *lenp) -{ - unsigned int hashval; - int len; - struct var *vp, **vpp; - const char *p = name; - - hashval = 0; - while (*p && *p != '=') - hashval = 2 * hashval + (unsigned char)*p++; - len = p - name; - - if (lenp) - *lenp = len; - vpp = &vartab[hashval % VTABSIZE]; - if (vppp) - *vppp = vpp; - - for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) { - if (vp->name_len != len) - continue; - if (memcmp(vp->text, name, len) != 0) - continue; - if (vppp) - *vppp = vpp; - return vp; - } - return NULL; -} diff --git a/sh/var.h b/sh/var.h deleted file mode 100644 index b7b7db8..0000000 --- a/sh/var.h +++ /dev/null @@ -1,131 +0,0 @@ -/* $NetBSD: var.h,v 1.23 2004/10/02 12:16:53 dsl 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 - * Kenneth Almquist. - * - * 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. - * - * @(#)var.h 8.2 (Berkeley) 5/4/95 - */ - -/* - * Shell variables. - */ - -/* flags */ -#define VEXPORT 0x01 /* variable is exported */ -#define VREADONLY 0x02 /* variable cannot be modified */ -#define VSTRFIXED 0x04 /* variable struct is statically allocated */ -#define VTEXTFIXED 0x08 /* text is statically allocated */ -#define VSTACK 0x10 /* text is allocated on the stack */ -#define VUNSET 0x20 /* the variable is not set */ -#define VNOFUNC 0x40 /* don't call the callback function */ -#define VNOSET 0x80 /* do not set variable - just readonly test */ - - -struct var { - struct var *next; /* next entry in hash list */ - int flags; /* flags are defined above */ - char *text; /* name=value */ - int name_len; /* length of name */ - void (*func)(const char *); - /* function to be called when */ - /* the variable gets set/unset */ -}; - - -struct localvar { - struct localvar *next; /* next local variable in list */ - struct var *vp; /* the variable that was made local */ - int flags; /* saved flags */ - char *text; /* saved text */ -}; - - -struct localvar *localvars; - -#if ATTY -extern struct var vatty; -#endif -extern struct var vifs; -extern struct var vmpath; -extern struct var vpath; -extern struct var vps1; -extern struct var vps2; -extern struct var vps4; -#ifdef WITH_HISTORY -extern struct var vterm; -extern struct var vtermcap; -extern struct var vhistsize; -#endif - -/* - * The following macros access the values of the above variables. - * They have to skip over the name. They return the null string - * for unset variables. - */ - -#define ifsval() (vifs.text + 4) -#define ifsset() ((vifs.flags & VUNSET) == 0) -#define mpathval() (vmpath.text + 9) -#define pathval() (vpath.text + 5) -#define ps1val() (vps1.text + 4) -#define ps2val() (vps2.text + 4) -#define ps4val() (vps4.text + 4) -#define optindval() (voptind.text + 7) -#ifdef WITH_HISTORY -#define histsizeval() (vhistsize.text + 9) -#define termval() (vterm.text + 5) -#endif - -#if ATTY -#define attyset() ((vatty.flags & VUNSET) == 0) -#endif -#define mpathset() ((vmpath.flags & VUNSET) == 0) - -void initvar(void); -void setvar(const char *, const char *, int); -void setvareq(char *, int); -struct strlist; -void listsetvar(struct strlist *, int); -char *lookupvar(const char *); -char *bltinlookup(const char *, int); -char **environment(void); -void shprocvar(void); -int showvars(const char *, int, int); -int exportcmd(int, char **); -int localcmd(int, char **); -void mklocal(const char *, int); -void listmklocal(struct strlist *, int); -void poplocalvars(void); -int setvarcmd(int, char **); -int unsetcmd(int, char **); -int unsetvar(const char *, int); -int setvarsafe(const char *, const char *, int); -void print_quoted(const char *); diff --git a/toolbox/Android.mk b/toolbox/Android.mk index 75ce53f..4fff9f5 100644 --- a/toolbox/Android.mk +++ b/toolbox/Android.mk @@ -12,7 +12,6 @@ TOOLS := \ rmmod \ lsmod \ ifconfig \ - setconsole \ rm \ mkdir \ rmdir \ @@ -89,6 +88,8 @@ LOCAL_SRC_FILES := \ LOCAL_C_INCLUDES := bionic/libc/bionic +LOCAL_CFLAGS += -Wno-unused-parameter + LOCAL_SHARED_LIBRARIES := \ libcutils \ liblog \ diff --git a/toolbox/date.c b/toolbox/date.c index 35ef846..d6c9052 100644 --- a/toolbox/date.c +++ b/toolbox/date.c @@ -6,15 +6,87 @@ #include <errno.h> #include <time.h> #include <linux/android_alarm.h> +#include <linux/rtc.h> #include <sys/ioctl.h> +static int settime_alarm(struct timespec *ts) { + int fd, ret; + + fd = open("/dev/alarm", O_RDWR); + if (fd < 0) + return fd; + + ret = ioctl(fd, ANDROID_ALARM_SET_RTC, ts); + close(fd); + return ret; +} + +static int settime_alarm_tm(struct tm *tm) { + time_t t; + struct timespec ts; + + t = mktime(tm); + ts.tv_sec = t; + ts.tv_nsec = 0; + return settime_alarm(&ts); +} + +static int settime_alarm_timeval(struct timeval *tv) { + struct timespec ts; + + ts.tv_sec = tv->tv_sec; + ts.tv_nsec = tv->tv_usec * 1000; + return settime_alarm(&ts); +} + +static int settime_rtc_tm(struct tm *tm) { + int fd, ret; + struct timeval tv; + struct rtc_time rtc; + + fd = open("/dev/rtc0", O_RDWR); + if (fd < 0) + return fd; + + tv.tv_sec = mktime(tm); + tv.tv_usec = 0; + + ret = settimeofday(&tv, NULL); + if (ret < 0) + goto done; + + memset(&rtc, 0, sizeof(rtc)); + rtc.tm_sec = tm->tm_sec; + rtc.tm_min = tm->tm_min; + rtc.tm_hour = tm->tm_hour; + rtc.tm_mday = tm->tm_mday; + rtc.tm_mon = tm->tm_mon; + rtc.tm_year = tm->tm_year; + rtc.tm_wday = tm->tm_wday; + rtc.tm_yday = tm->tm_yday; + rtc.tm_isdst = tm->tm_isdst; + + ret = ioctl(fd, RTC_SET_TIME, rtc); +done: + close(fd); + return ret; +} + +static int settime_rtc_timeval(struct timeval *tv) { + struct tm tm, *err; + time_t t = tv->tv_sec; + + err = gmtime_r(&t, &tm); + if (!err) + return -1; + + return settime_rtc_tm(&tm); +} + static void settime(char *s) { struct tm tm; int day = atoi(s); int hour; - time_t t; - int fd; - struct timespec ts; while (*s && *s != '.') s++; @@ -32,12 +104,8 @@ static void settime(char *s) { tm.tm_sec = (hour % 100); tm.tm_isdst = -1; - t = mktime(&tm); - - fd = open("/dev/alarm", O_RDWR); - ts.tv_sec = t; - ts.tv_nsec = 0; - ioctl(fd, ANDROID_ALARM_SET_RTC, &ts); + if (settime_alarm_tm(&tm) < 0) + settime_rtc_tm(&tm); } int date_main(int argc, char *argv[]) @@ -114,12 +182,10 @@ int date_main(int argc, char *argv[]) //tv.tv_sec = mktime(&tm); //tv.tv_usec = 0; strtotimeval(argv[optind], &tv); - printf("time %s -> %d.%d\n", argv[optind], tv.tv_sec, tv.tv_usec); - fd = open("/dev/alarm", O_RDWR); - ts.tv_sec = tv.tv_sec; - ts.tv_nsec = tv.tv_usec * 1000; - res = ioctl(fd, ANDROID_ALARM_SET_RTC, &ts); - //res = settimeofday(&tv, NULL); + printf("time %s -> %lu.%lu\n", argv[optind], tv.tv_sec, tv.tv_usec); + res = settime_alarm_timeval(&tv); + if (res < 0) + res = settime_rtc_timeval(&tv); if(res < 0) { fprintf(stderr,"settimeofday failed %s\n", strerror(errno)); return 1; diff --git a/toolbox/dd.c b/toolbox/dd.c index a8c12d2..6b61ffb 100644 --- a/toolbox/dd.c +++ b/toolbox/dd.c @@ -92,9 +92,6 @@ extern u_int files_cnt; extern int progress; extern const u_char *ctab; - -#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); diff --git a/toolbox/getevent.c b/toolbox/getevent.c index 5f5e16b..ed381f5 100644 --- a/toolbox/getevent.c +++ b/toolbox/getevent.c @@ -166,7 +166,7 @@ static int print_possible_events(int fd, int print_flags) if(bit_labels && (print_flags & PRINT_LABELS)) { bit_label = get_label(bit_labels, j * 8 + k); if(bit_label) - printf(" %.20s%c%*s", bit_label, down, 20 - strlen(bit_label), ""); + printf(" %.20s%c%*s", bit_label, down, (int) (20 - strlen(bit_label)), ""); else printf(" %04x%c ", j * 8 + k, down); } else { diff --git a/toolbox/insmod.c b/toolbox/insmod.c index 756a64b..fb1448b 100644 --- a/toolbox/insmod.c +++ b/toolbox/insmod.c @@ -4,6 +4,7 @@ #include <unistd.h> #include <malloc.h> #include <errno.h> +#include <sys/param.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> @@ -45,7 +46,6 @@ bail: return buffer; } -#define min(x,y) ((x) < (y) ? (x) : (y)) int insmod_main(int argc, char **argv) { void *file; @@ -73,7 +73,7 @@ int insmod_main(int argc, char **argv) char *ptr = opts; for (i = 2; (i < argc) && (ptr < end); i++) { - len = min(strlen(argv[i]), end - ptr); + len = MIN(strlen(argv[i]), end - ptr); memcpy(ptr, argv[i], len); ptr += len; *ptr++ = ' '; diff --git a/toolbox/ls.c b/toolbox/ls.c index 5324511..3cc5bb2 100644 --- a/toolbox/ls.c +++ b/toolbox/ls.c @@ -75,23 +75,23 @@ static void mode2str(unsigned mode, char *out) *out = 0; } -static void user2str(unsigned uid, char *out) +static void user2str(uid_t uid, char *out, size_t out_size) { struct passwd *pw = getpwuid(uid); if(pw) { - strcpy(out, pw->pw_name); + strlcpy(out, pw->pw_name, out_size); } else { - sprintf(out, "%d", uid); + snprintf(out, out_size, "%d", uid); } } -static void group2str(unsigned gid, char *out) +static void group2str(gid_t gid, char *out, size_t out_size) { struct group *gr = getgrgid(gid); if(gr) { - strcpy(out, gr->gr_name); + strlcpy(out, gr->gr_name, out_size); } else { - sprintf(out, "%d", gid); + snprintf(out, out_size, "%d", gid); } } @@ -164,8 +164,8 @@ static int listfile_long(const char *path, struct stat *s, int flags) { char date[32]; char mode[16]; - char user[16]; - char group[16]; + char user[32]; + char group[32]; const char *name; if(!s || !path) { @@ -182,11 +182,11 @@ static int listfile_long(const char *path, struct stat *s, int flags) mode2str(s->st_mode, mode); if (flags & LIST_LONG_NUMERIC) { - sprintf(user, "%ld", s->st_uid); - sprintf(group, "%ld", s->st_gid); + snprintf(user, sizeof(user), "%u", s->st_uid); + snprintf(group, sizeof(group), "%u", s->st_gid); } else { - user2str(s->st_uid, user); - group2str(s->st_gid, group); + user2str(s->st_uid, user, sizeof(user)); + group2str(s->st_gid, group, sizeof(group)); } strftime(date, 32, "%Y-%m-%d %H:%M", localtime((const time_t*)&s->st_mtime)); @@ -209,7 +209,7 @@ static int listfile_long(const char *path, struct stat *s, int flags) break; case S_IFLNK: { char linkto[256]; - int len; + ssize_t len; len = readlink(path, linkto, 256); if(len < 0) return -1; @@ -235,11 +235,11 @@ static int listfile_long(const char *path, struct stat *s, int flags) return 0; } -static int listfile_maclabel(const char *path, struct stat *s, int flags) +static int listfile_maclabel(const char *path, struct stat *s) { char mode[16]; - char user[16]; - char group[16]; + char user[32]; + char group[32]; char *maclabel = NULL; const char *name; @@ -261,8 +261,8 @@ static int listfile_maclabel(const char *path, struct stat *s, int flags) } mode2str(s->st_mode, mode); - user2str(s->st_uid, user); - group2str(s->st_gid, group); + user2str(s->st_uid, user, sizeof(user)); + group2str(s->st_gid, group, sizeof(group)); switch(s->st_mode & S_IFMT) { case S_IFLNK: { @@ -316,6 +316,7 @@ static int listfile(const char *dirname, const char *filename, int flags) } if(lstat(pathname, &s) < 0) { + fprintf(stderr, "lstat '%s' failed: %s\n", pathname, strerror(errno)); return -1; } @@ -324,7 +325,7 @@ static int listfile(const char *dirname, const char *filename, int flags) } if ((flags & LIST_MACLABEL) != 0) { - return listfile_maclabel(pathname, &s, flags); + return listfile_maclabel(pathname, &s); } else if ((flags & LIST_LONG) != 0) { return listfile_long(pathname, &s, flags); } else /*((flags & LIST_SIZE) != 0)*/ { diff --git a/toolbox/nandread.c b/toolbox/nandread.c index b124731..d43b2fe 100644 --- a/toolbox/nandread.c +++ b/toolbox/nandread.c @@ -12,7 +12,7 @@ static int test_empty(const char *buf, size_t size) { while(size--) { - if (*buf++ != 0xff) + if (*buf++ != (char) 0xff) return 0; } return 1; @@ -44,7 +44,7 @@ int nandread_main(int argc, char **argv) struct mtd_info_user mtdinfo; struct mtd_ecc_stats initial_ecc, last_ecc, ecc; struct mtd_oob_buf oobbuf; - struct nand_ecclayout ecclayout; + nand_ecclayout_t ecclayout; do { c = getopt(argc, argv, "d:f:s:S:L:Rhv"); @@ -158,7 +158,7 @@ int nandread_main(int argc, char **argv) printf("oobavail: %u\n", ecclayout.oobavail); } if (ecclayout.oobavail > spare_size) - printf("oobavail, %d > image spare size, %d\n", ecclayout.oobavail, spare_size); + printf("oobavail, %d > image spare size, %zu\n", ecclayout.oobavail, spare_size); ret = ioctl(fd, ECCGETSTATS, &initial_ecc); if (ret) { @@ -177,7 +177,11 @@ int nandread_main(int argc, char **argv) if (rawmode) { rawmode = mtdinfo.oobsize; +#if !defined(MTD_STUPID_LOCK) /* using uapi kernel headers */ + ret = ioctl(fd, MTDFILEMODE, MTD_FILE_MODE_RAW); +#else /* still using old kernel headers */ ret = ioctl(fd, MTDFILEMODE, MTD_MODE_RAW); +#endif if (ret) { fprintf(stderr, "failed set raw mode for %s, %s\n", devname, strerror(errno)); diff --git a/toolbox/newfs_msdos.c b/toolbox/newfs_msdos.c index 6d78eb6..27dca42 100644 --- a/toolbox/newfs_msdos.c +++ b/toolbox/newfs_msdos.c @@ -234,13 +234,6 @@ static void mklabel(u_int8_t *, const char *); static void setstr(u_int8_t *, const char *, size_t); static void usage(void); -#ifdef ANDROID -#define powerof2(x) ((((x) - 1) & (x)) == 0) -#define howmany(x, y) (((x) + ((y) - 1)) / (y)) -#define MAX(x,y) ((x) > (y) ? (x) : (y)) -#define MIN(a, b) ((a) < (b) ? (a) : (b)) - -#endif /* * Construct a FAT12, FAT16, or FAT32 file system. */ diff --git a/toolbox/r.c b/toolbox/r.c index eb8ea0b..3b80db7 100644 --- a/toolbox/r.c +++ b/toolbox/r.c @@ -1,8 +1,16 @@ +#include <fcntl.h> +#include <inttypes.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> -#include <sys/mman.h> -#include <fcntl.h> #include <string.h> +#include <sys/mman.h> + +#if __LP64__ +#define strtoptr strtoull +#else +#define strtoptr strtoul +#endif static int usage() { @@ -12,14 +20,9 @@ static int usage() int r_main(int argc, char *argv[]) { - int width = 4, set = 0, fd; - unsigned addr, value, endaddr = 0; - unsigned long mmap_start, mmap_size; - void *page; - char *end; - if(argc < 2) return usage(); + int width = 4; if(!strcmp(argv[1], "-b")) { width = 1; argc--; @@ -31,37 +34,40 @@ int r_main(int argc, char *argv[]) } if(argc < 2) return usage(); - addr = strtoul(argv[1], 0, 16); + uintptr_t addr = strtoptr(argv[1], 0, 16); - end = strchr(argv[1], '-'); + uintptr_t endaddr = 0; + char* end = strchr(argv[1], '-'); if (end) - endaddr = strtoul(end + 1, 0, 16); + endaddr = strtoptr(end + 1, 0, 16); if (!endaddr) endaddr = addr + width - 1; if (endaddr <= addr) { - fprintf(stderr, "invalid end address\n"); + fprintf(stderr, "end address <= start address\n"); return -1; } + bool set = false; + uint32_t value = 0; if(argc > 2) { - set = 1; + set = true; value = strtoul(argv[2], 0, 16); } - fd = open("/dev/mem", O_RDWR | O_SYNC); + int fd = open("/dev/mem", O_RDWR | O_SYNC); if(fd < 0) { fprintf(stderr,"cannot open /dev/mem\n"); return -1; } - - mmap_start = addr & ~(PAGE_SIZE - 1); - mmap_size = endaddr - mmap_start + 1; + + off64_t mmap_start = addr & ~(PAGE_SIZE - 1); + size_t mmap_size = endaddr - mmap_start + 1; mmap_size = (mmap_size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); - page = mmap(0, mmap_size, PROT_READ | PROT_WRITE, - MAP_SHARED, fd, mmap_start); + void* page = mmap64(0, mmap_size, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, mmap_start); if(page == MAP_FAILED){ fprintf(stderr,"cannot mmap region\n"); @@ -71,21 +77,21 @@ int r_main(int argc, char *argv[]) while (addr <= endaddr) { switch(width){ case 4: { - unsigned *x = (unsigned*) (((unsigned) page) + (addr & 4095)); + uint32_t* x = (uint32_t*) (((uintptr_t) page) + (addr & 4095)); if(set) *x = value; - fprintf(stderr,"%08x: %08x\n", addr, *x); + fprintf(stderr,"%08"PRIxPTR": %08x\n", addr, *x); break; } case 2: { - unsigned short *x = (unsigned short*) (((unsigned) page) + (addr & 4095)); + uint16_t* x = (uint16_t*) (((uintptr_t) page) + (addr & 4095)); if(set) *x = value; - fprintf(stderr,"%08x: %04x\n", addr, *x); + fprintf(stderr,"%08"PRIxPTR": %04x\n", addr, *x); break; } case 1: { - unsigned char *x = (unsigned char*) (((unsigned) page) + (addr & 4095)); + uint8_t* x = (uint8_t*) (((uintptr_t) page) + (addr & 4095)); if(set) *x = value; - fprintf(stderr,"%08x: %02x\n", addr, *x); + fprintf(stderr,"%08"PRIxPTR": %02x\n", addr, *x); break; } } diff --git a/toolbox/schedtop.c b/toolbox/schedtop.c index 6859b50..a76f968 100644 --- a/toolbox/schedtop.c +++ b/toolbox/schedtop.c @@ -212,7 +212,7 @@ static void update_table(DIR *d, uint32_t flags) } if (!(flags & FLAG_BATCH)) printf("\e[H\e[0J"); - printf("Processes: %d, Threads %d\n", processes.active, threads.active); + printf("Processes: %zu, Threads %zu\n", processes.active, threads.active); switch (time_dp) { case 3: printf(" TID --- SINCE LAST ---- ---------- TOTAL ----------\n"); diff --git a/toolbox/setconsole.c b/toolbox/setconsole.c deleted file mode 100644 index 0159c07..0000000 --- a/toolbox/setconsole.c +++ /dev/null @@ -1,166 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <fcntl.h> -#include <string.h> -#include <linux/kd.h> -#include <linux/vt.h> -#include <errno.h> -#include <pthread.h> -#include <unistd.h> -#include <sys/ioctl.h> - -static int activate_thread_switch_vc; -static void *activate_thread(void *arg) -{ - int res; - int fd = (int)arg; - while(activate_thread_switch_vc >= 0) { - do { - res = ioctl(fd, VT_ACTIVATE, (void*)activate_thread_switch_vc); - } while(res < 0 && errno == EINTR); - if (res < 0) { - fprintf(stderr, "ioctl( vcfd, VT_ACTIVATE, vtnum) failed, %d %d %s for %d\n", res, errno, strerror(errno), activate_thread_switch_vc); - } - if(activate_thread_switch_vc >= 0) - sleep(1); - } - return NULL; -} - - -int setconsole_main(int argc, char *argv[]) -{ - int c; - int fd; - int res; - - int mode = -1; - int new_vc = 0; - int close_vc = 0; - int switch_vc = -1; - int printvc = 0; - char *ttydev = "/dev/tty0"; - - do { - c = getopt(argc, argv, "d:gtncv:poh"); - if (c == EOF) - break; - switch (c) { - case 'd': - ttydev = optarg; - break; - case 'g': - if(mode == KD_TEXT) { - fprintf(stderr, "%s: cannot specify both -g and -t\n", argv[0]); - exit(1); - } - mode = KD_GRAPHICS; - break; - case 't': - if(mode == KD_GRAPHICS) { - fprintf(stderr, "%s: cannot specify both -g and -t\n", argv[0]); - exit(1); - } - mode = KD_TEXT; - break; - case 'n': - new_vc = 1; - break; - case 'c': - close_vc = 1; - break; - case 'v': - switch_vc = atoi(optarg); - break; - case 'p': - printvc |= 1; - break; - case 'o': - printvc |= 2; - break; - case 'h': - fprintf(stderr, "%s [-d <dev>] [-v <vc>] [-gtncpoh]\n" - " -d <dev> Use <dev> instead of /dev/tty0\n" - " -v <vc> Switch to virtual console <vc>\n" - " -g Switch to graphics mode\n" - " -t Switch to text mode\n" - " -n Create and switch to new virtual console\n" - " -c Close unused virtual consoles\n" - " -p Print new virtual console\n" - " -o Print old virtual console\n" - " -h Print help\n", argv[0]); - return -1; - case '?': - fprintf(stderr, "%s: invalid option -%c\n", - argv[0], optopt); - exit(1); - } - } while (1); - if(mode == -1 && new_vc == 0 && close_vc == 0 && switch_vc == -1 && printvc == 0) { - fprintf(stderr,"%s [-d <dev>] [-v <vc>] [-gtncpoh]\n", argv[0]); - return -1; - } - - fd = open(ttydev, O_RDWR | O_SYNC); - if (fd < 0) { - fprintf(stderr, "cannot open %s\n", ttydev); - return -1; - } - - if ((printvc && !new_vc) || (printvc & 2)) { - struct vt_stat vs; - - res = ioctl(fd, VT_GETSTATE, &vs); - if (res < 0) { - fprintf(stderr, "ioctl(vcfd, VT_GETSTATE, &vs) failed, %d\n", res); - } - printf("%d\n", vs.v_active); - } - - if (new_vc) { - int vtnum; - res = ioctl(fd, VT_OPENQRY, &vtnum); - if (res < 0 || vtnum == -1) { - fprintf(stderr, "ioctl(vcfd, VT_OPENQRY, &vtnum) failed, res %d, vtnum %d\n", res, vtnum); - } - switch_vc = vtnum; - } - if (switch_vc != -1) { - pthread_t thread; - pthread_attr_t attr; - activate_thread_switch_vc = switch_vc; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - pthread_create(&thread, &attr, activate_thread, (void*)fd); - - do { - res = ioctl(fd, VT_WAITACTIVE, (void*)switch_vc); - } while(res < 0 && errno == EINTR); - activate_thread_switch_vc = -1; - if (res < 0) { - fprintf(stderr, "ioctl( vcfd, VT_WAITACTIVE, vtnum) failed, %d %d %s for %d\n", res, errno, strerror(errno), switch_vc); - } - if(printvc & 1) - printf("%d\n", switch_vc); - - close(fd); - fd = open(ttydev, O_RDWR | O_SYNC); - if (fd < 0) { - fprintf(stderr, "cannot open %s\n", ttydev); - return -1; - } - } - if (close_vc) { - res = ioctl(fd, VT_DISALLOCATE, 0); - if (res < 0) { - fprintf(stderr, "ioctl(vcfd, VT_DISALLOCATE, 0) failed, %d\n", res); - } - } - if (mode != -1) { - if (ioctl(fd, KDSETMODE, (void*)mode) < 0) { - fprintf(stderr, "KDSETMODE %d failed\n", mode); - return -1; - } - } - return 0; -} diff --git a/toolbox/swapon.c b/toolbox/swapon.c index afa6868..a810b3d 100644 --- a/toolbox/swapon.c +++ b/toolbox/swapon.c @@ -5,12 +5,6 @@ #include <asm/page.h> #include <sys/swap.h> -/* XXX These need to be obtained from kernel headers. See b/9336527 */ -#define SWAP_FLAG_PREFER 0x8000 -#define SWAP_FLAG_PRIO_MASK 0x7fff -#define SWAP_FLAG_PRIO_SHIFT 0 -#define SWAP_FLAG_DISCARD 0x10000 - void usage(char *name) { fprintf(stderr, "Usage: %s [-p prio] <filename>\n" diff --git a/toolbox/uptime.c b/toolbox/uptime.c index 1c312b0..3fb4606 100644 --- a/toolbox/uptime.c +++ b/toolbox/uptime.c @@ -54,24 +54,35 @@ static void format_time(int time, char* buffer) { sprintf(buffer, "%02d:%02d:%02d", hours, minutes, seconds); } -int64_t elapsedRealtime() +static int elapsedRealtimeAlarm(struct timespec *ts) { - struct timespec ts; int fd, result; fd = open("/dev/alarm", O_RDONLY); if (fd < 0) return fd; - result = ioctl(fd, ANDROID_ALARM_GET_TIME(ANDROID_ALARM_ELAPSED_REALTIME), &ts); - close(fd); + result = ioctl(fd, ANDROID_ALARM_GET_TIME(ANDROID_ALARM_ELAPSED_REALTIME), ts); + close(fd); + + return result; +} + +int64_t elapsedRealtime() +{ + struct timespec ts; + + int result = elapsedRealtimeAlarm(&ts); + if (result < 0) + result = clock_gettime(CLOCK_BOOTTIME, &ts); if (result == 0) return ts.tv_sec; return -1; } -int uptime_main(int argc, char *argv[]) +int uptime_main(int argc __attribute__((unused)), + char *argv[] __attribute__((unused))) { float up_time, idle_time; char up_string[100], idle_string[100], sleep_string[100]; |