diff --git a/debuginfod/debuginfod-client.c b/debuginfod/debuginfod-client.c index e5a2e82..6e62b86 100644 --- a/debuginfod/debuginfod-client.c +++ b/debuginfod/debuginfod-client.c @@ -1,5 +1,5 @@ /* Retrieve ELF / DWARF / source files from the debuginfod. - Copyright (C) 2019-2020 Red Hat, Inc. + Copyright (C) 2019 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -40,7 +40,6 @@ #include "config.h" #include "debuginfod.h" -#include "system.h" #include #include #include @@ -58,7 +57,6 @@ #include #include #include -#include #include /* If fts.h is included before config.h, its indirect inclusions may not @@ -100,15 +98,16 @@ static const time_t cache_default_max_unused_age_s = 604800; /* 1 week */ static const char *cache_default_name = ".debuginfod_client_cache"; static const char *cache_path_envvar = DEBUGINFOD_CACHE_PATH_ENV_VAR; -/* URLs of debuginfods, separated by url_delim. */ +/* URLs of debuginfods, separated by url_delim. + This env var must be set for debuginfod-client to run. */ static const char *server_urls_envvar = DEBUGINFOD_URLS_ENV_VAR; static const char *url_delim = " "; static const char url_delim_char = ' '; -/* Timeout for debuginfods, in seconds (to get at least 100K). */ +/* Timeout for debuginfods, in seconds. + This env var must be set for debuginfod-client to run. */ static const char *server_timeout_envvar = DEBUGINFOD_TIMEOUT_ENV_VAR; -static const long default_timeout = 90; - +static int server_timeout = 5; /* Data associated with a particular CURL easy handle. Passed to the write callback. */ @@ -279,87 +278,6 @@ debuginfod_clean_cache(debuginfod_client *c, #define MAX_BUILD_ID_BYTES 64 -static void -add_extra_headers(CURL *handle) -{ - /* Compute a User-Agent: string to send. The more accurately this - describes this host, the likelier that the debuginfod servers - might be able to locate debuginfo for us. */ - - char* utspart = NULL; - struct utsname uts; - int rc = 0; - rc = uname (&uts); - if (rc == 0) - rc = asprintf(& utspart, "%s/%s", uts.sysname, uts.machine); - if (rc < 0) - utspart = NULL; - - FILE *f = fopen ("/etc/os-release", "r"); - if (f == NULL) - f = fopen ("/usr/lib/os-release", "r"); - char *id = NULL; - char *version = NULL; - if (f != NULL) - { - while (id == NULL || version == NULL) - { - char buf[128]; - char *s = &buf[0]; - if (fgets (s, sizeof(buf), f) == NULL) - break; - - int len = strlen (s); - if (len < 3) - continue; - if (s[len - 1] == '\n') - { - s[len - 1] = '\0'; - len--; - } - - char *v = strchr (s, '='); - if (v == NULL || strlen (v) < 2) - continue; - - /* Split var and value. */ - *v = '\0'; - v++; - - /* Remove optional quotes around value string. */ - if (*v == '"' || *v == '\'') - { - v++; - s[len - 1] = '\0'; - } - if (strcmp (s, "ID") == 0) - id = strdup (v); - if (strcmp (s, "VERSION_ID") == 0) - version = strdup (v); - } - fclose (f); - } - - char *ua = NULL; - rc = asprintf(& ua, "%s/%s,%s,%s/%s", - PACKAGE_NAME, PACKAGE_VERSION, - utspart ?: "", - id ?: "", - version ?: ""); - if (rc < 0) - ua = NULL; - - if (ua) - curl_easy_setopt(handle, CURLOPT_USERAGENT, (void*) ua); /* implicit strdup */ - - free (ua); - free (id); - free (version); - free (utspart); -} - - - /* Query each of the server URLs found in $DEBUGINFOD_URLS for the file with the specified build-id, type (debuginfo, executable or source) and filename. filename may be NULL. If found, return a file @@ -383,16 +301,6 @@ debuginfod_query_server (debuginfod_client *c, char target_cache_tmppath[PATH_MAX*5]; char suffix[PATH_MAX*2]; char build_id_bytes[MAX_BUILD_ID_BYTES * 2 + 1]; - int rc; - - /* Is there any server we can query? If not, don't do any work, - just return with ENOSYS. Don't even access the cache. */ - urls_envvar = getenv(server_urls_envvar); - if (urls_envvar == NULL || urls_envvar[0] == '\0') - { - rc = -ENOSYS; - goto out; - } /* Copy lowercase hex representation of build_id into buf. */ if ((build_id_len >= MAX_BUILD_ID_BYTES) || @@ -465,7 +373,7 @@ debuginfod_query_server (debuginfod_client *c, /* XXX combine these */ snprintf(interval_path, sizeof(interval_path), "%s/%s", cache_path, cache_clean_interval_filename); snprintf(maxage_path, sizeof(maxage_path), "%s/%s", cache_path, cache_max_unused_age_filename); - rc = debuginfod_init_cache(cache_path, interval_path, maxage_path); + int rc = debuginfod_init_cache(cache_path, interval_path, maxage_path); if (rc != 0) goto out; rc = debuginfod_clean_cache(c, cache_path, interval_path, maxage_path); @@ -482,10 +390,16 @@ debuginfod_query_server (debuginfod_client *c, return fd; } - long timeout = default_timeout; - const char* timeout_envvar = getenv(server_timeout_envvar); - if (timeout_envvar != NULL) - timeout = atoi (timeout_envvar); + + urls_envvar = getenv(server_urls_envvar); + if (urls_envvar == NULL || urls_envvar[0] == '\0') + { + rc = -ENOSYS; + goto out; + } + + if (getenv(server_timeout_envvar)) + server_timeout = atoi (getenv(server_timeout_envvar)); /* make a copy of the envvar so it can be safely modified. */ server_urls = strdup(urls_envvar); @@ -577,22 +491,14 @@ debuginfod_query_server (debuginfod_client *c, CURLOPT_WRITEFUNCTION, debuginfod_write_callback); curl_easy_setopt(data[i].handle, CURLOPT_WRITEDATA, (void*)&data[i]); - if (timeout > 0) - { - /* Make sure there is at least some progress, - try to get at least 100K per timeout seconds. */ - curl_easy_setopt (data[i].handle, CURLOPT_LOW_SPEED_TIME, - timeout); - curl_easy_setopt (data[i].handle, CURLOPT_LOW_SPEED_LIMIT, - 100 * 1024L); - } + curl_easy_setopt(data[i].handle, CURLOPT_TIMEOUT, (long) server_timeout); curl_easy_setopt(data[i].handle, CURLOPT_FILETIME, (long) 1); curl_easy_setopt(data[i].handle, CURLOPT_FOLLOWLOCATION, (long) 1); curl_easy_setopt(data[i].handle, CURLOPT_FAILONERROR, (long) 1); curl_easy_setopt(data[i].handle, CURLOPT_NOSIGNAL, (long) 1); curl_easy_setopt(data[i].handle, CURLOPT_AUTOREFERER, (long) 1); curl_easy_setopt(data[i].handle, CURLOPT_ACCEPT_ENCODING, ""); - add_extra_headers(data[i].handle); + curl_easy_setopt(data[i].handle, CURLOPT_USERAGENT, (void*) PACKAGE_STRING); curl_multi_add_handle(curlm, data[i].handle); server_url = strtok_r(NULL, url_delim, &strtok_saveptr); @@ -603,35 +509,15 @@ debuginfod_query_server (debuginfod_client *c, long loops = 0; do { - /* Wait 1 second, the minimum DEBUGINFOD_TIMEOUT. */ - curl_multi_wait(curlm, NULL, 0, 1000, NULL); - - /* If the target file has been found, abort the other queries. */ - if (target_handle != NULL) - for (int i = 0; i < num_urls; i++) - if (data[i].handle != target_handle) - curl_multi_remove_handle(curlm, data[i].handle); - - CURLMcode curlm_res = curl_multi_perform(curlm, &still_running); - if (curlm_res != CURLM_OK) - { - switch (curlm_res) - { - case CURLM_CALL_MULTI_PERFORM: continue; - case CURLM_OUT_OF_MEMORY: rc = -ENOMEM; break; - default: rc = -ENETUNREACH; break; - } - goto out1; - } + CURLMcode curl_res; if (c->progressfn) /* inform/check progress callback */ { loops ++; long pa = loops; /* default params for progress callback */ - long pb = 0; /* transfer_timeout tempting, but loops != elapsed-time */ + long pb = 0; if (target_handle) /* we've committed to a server; report its download progress */ { - CURLcode curl_res; #ifdef CURLINFO_SIZE_DOWNLOAD_T curl_off_t dl; curl_res = curl_easy_getinfo(target_handle, @@ -648,8 +534,6 @@ debuginfod_query_server (debuginfod_client *c, pa = (dl > LONG_MAX ? LONG_MAX : (long)dl); #endif - /* NB: If going through deflate-compressing proxies, this - number is likely to be unavailable, so -1 may show. */ #ifdef CURLINFO_CURLINFO_CONTENT_LENGTH_DOWNLOAD_T curl_off_t cl; curl_res = curl_easy_getinfo(target_handle, @@ -670,6 +554,27 @@ debuginfod_query_server (debuginfod_client *c, if ((*c->progressfn) (c, pa, pb)) break; } + + /* Wait 1 second, the minimum DEBUGINFOD_TIMEOUT. */ + curl_multi_wait(curlm, NULL, 0, 1000, NULL); + + /* If the target file has been found, abort the other queries. */ + if (target_handle != NULL) + for (int i = 0; i < num_urls; i++) + if (data[i].handle != target_handle) + curl_multi_remove_handle(curlm, data[i].handle); + + curl_res = curl_multi_perform(curlm, &still_running); + if (curl_res != CURLM_OK) + { + switch (curl_res) + { + case CURLM_CALL_MULTI_PERFORM: continue; + case CURLM_OUT_OF_MEMORY: rc = -ENOMEM; break; + default: rc = -ENETUNREACH; break; + } + goto out1; + } } while (still_running); /* Check whether a query was successful. If so, assign its handle @@ -768,9 +673,9 @@ debuginfod_query_server (debuginfod_client *c, curl_multi_cleanup(curlm); unlink (target_cache_tmppath); - close (fd); /* before the rmdir, otherwise it'll fail */ (void) rmdir (target_cache_dir); /* nop if not empty */ free(data); + close (fd); out0: free (server_urls); @@ -779,22 +684,6 @@ debuginfod_query_server (debuginfod_client *c, return rc; } - -/* Activate a basic form of progress tracing */ -static int -default_progressfn (debuginfod_client *c, long a, long b) -{ - (void) c; - - dprintf(STDERR_FILENO, - "Downloading from debuginfod %ld/%ld%s", a, b, - ((a == b) ? "\n" : "\r")); - /* XXX: include URL - stateful */ - - return 0; -} - - /* See debuginfod.h */ debuginfod_client * debuginfod_begin (void) @@ -803,12 +692,7 @@ debuginfod_begin (void) size_t size = sizeof (struct debuginfod_client); client = (debuginfod_client *) malloc (size); if (client != NULL) - { - if (getenv(DEBUGINFOD_PROGRESS_ENV_VAR)) - client->progressfn = default_progressfn; - else - client->progressfn = NULL; - } + client->progressfn = NULL; return client; } diff --git a/debuginfod/debuginfod-client.c.curl-gcc-10 b/debuginfod/debuginfod-client.c.curl-gcc-10 deleted file mode 100644 index 4fe7d72..0000000 --- a/debuginfod/debuginfod-client.c.curl-gcc-10 +++ /dev/null @@ -1,756 +0,0 @@ -/* Retrieve ELF / DWARF / source files from the debuginfod. - Copyright (C) 2019 Red Hat, Inc. - This file is part of elfutils. - - This file is free software; you can redistribute it and/or modify - it under the terms of either - - * the GNU Lesser General Public License as published by the Free - Software Foundation; either version 3 of the License, or (at - your option) any later version - - or - - * the GNU General Public License as published by the Free - Software Foundation; either version 2 of the License, or (at - your option) any later version - - or both in parallel, as here. - - elfutils 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 copies of the GNU General Public License and - the GNU Lesser General Public License along with this program. If - not, see . */ - - -/* cargo-cult from libdwfl linux-kernel-modules.c */ -/* In case we have a bad fts we include this before config.h because it - can't handle _FILE_OFFSET_BITS. - Everything we need here is fine if its declarations just come first. - Also, include sys/types.h before fts. On some systems fts.h is not self - contained. */ -#ifdef BAD_FTS - #include - #include -#endif - -#include "config.h" -#include "debuginfod.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* If fts.h is included before config.h, its indirect inclusions may not - give us the right LFS aliases of these functions, so map them manually. */ -#ifdef BAD_FTS - #ifdef _FILE_OFFSET_BITS - #define open open64 - #define fopen fopen64 - #endif -#else - #include - #include -#endif - -struct debuginfod_client -{ - /* Progress/interrupt callback function. */ - debuginfod_progressfn_t progressfn; - - /* Can contain all other context, like cache_path, server_urls, - timeout or other info gotten from environment variables, the - handle data, etc. So those don't have to be reparsed and - recreated on each request. */ -}; - -/* The cache_clean_interval_s file within the debuginfod cache specifies - how frequently the cache should be cleaned. The file's st_mtime represents - the time of last cleaning. */ -static const char *cache_clean_interval_filename = "cache_clean_interval_s"; -static const time_t cache_clean_default_interval_s = 86400; /* 1 day */ - -/* The cache_max_unused_age_s file within the debuginfod cache specifies the - the maximum time since last access that a file will remain in the cache. */ -static const char *cache_max_unused_age_filename = "max_unused_age_s"; -static const time_t cache_default_max_unused_age_s = 604800; /* 1 week */ - -/* Location of the cache of files downloaded from debuginfods. - The default parent directory is $HOME, or '/' if $HOME doesn't exist. */ -static const char *cache_default_name = ".debuginfod_client_cache"; -static const char *cache_path_envvar = DEBUGINFOD_CACHE_PATH_ENV_VAR; - -/* URLs of debuginfods, separated by url_delim. - This env var must be set for debuginfod-client to run. */ -static const char *server_urls_envvar = DEBUGINFOD_URLS_ENV_VAR; -static const char *url_delim = " "; -static const char url_delim_char = ' '; - -/* Timeout for debuginfods, in seconds. - This env var must be set for debuginfod-client to run. */ -static const char *server_timeout_envvar = DEBUGINFOD_TIMEOUT_ENV_VAR; -static int server_timeout = 5; - -/* Data associated with a particular CURL easy handle. Passed to - the write callback. */ -struct handle_data -{ - /* Cache file to be written to in case query is successful. */ - int fd; - - /* URL queried by this handle. */ - char url[PATH_MAX]; - - /* This handle. */ - CURL *handle; - - /* Pointer to handle that should write to fd. Initially points to NULL, - then points to the first handle that begins writing the target file - to the cache. Used to ensure that a file is not downloaded from - multiple servers unnecessarily. */ - CURL **target_handle; -}; - -static size_t -debuginfod_write_callback (char *ptr, size_t size, size_t nmemb, void *data) -{ - ssize_t count = size * nmemb; - - struct handle_data *d = (struct handle_data*)data; - - /* Indicate to other handles that they can abort their transfer. */ - if (*d->target_handle == NULL) - *d->target_handle = d->handle; - - /* If this handle isn't the target handle, abort transfer. */ - if (*d->target_handle != d->handle) - return -1; - - return (size_t) write(d->fd, (void*)ptr, count); -} - -/* Create the cache and interval file if they do not already exist. - Return 0 if cache and config file are initialized, otherwise return - the appropriate error code. */ -static int -debuginfod_init_cache (char *cache_path, char *interval_path, char *maxage_path) -{ - struct stat st; - - /* If the cache and config file already exist then we are done. */ - if (stat(cache_path, &st) == 0 && stat(interval_path, &st) == 0) - return 0; - - /* Create the cache and config files as necessary. */ - if (stat(cache_path, &st) != 0 && mkdir(cache_path, 0777) < 0) - return -errno; - - int fd = -1; - - /* init cleaning interval config file. */ - fd = open(interval_path, O_CREAT | O_RDWR, 0666); - if (fd < 0) - return -errno; - - if (dprintf(fd, "%ld", cache_clean_default_interval_s) < 0) - return -errno; - - /* init max age config file. */ - if (stat(maxage_path, &st) != 0 - && (fd = open(maxage_path, O_CREAT | O_RDWR, 0666)) < 0) - return -errno; - - if (dprintf(fd, "%ld", cache_default_max_unused_age_s) < 0) - return -errno; - - return 0; -} - - -/* Delete any files that have been unmodied for a period - longer than $DEBUGINFOD_CACHE_CLEAN_INTERVAL_S. */ -static int -debuginfod_clean_cache(debuginfod_client *c, - char *cache_path, char *interval_path, - char *max_unused_path) -{ - struct stat st; - FILE *interval_file; - FILE *max_unused_file; - - if (stat(interval_path, &st) == -1) - { - /* Create new interval file. */ - interval_file = fopen(interval_path, "w"); - - if (interval_file == NULL) - return -errno; - - int rc = fprintf(interval_file, "%ld", cache_clean_default_interval_s); - fclose(interval_file); - - if (rc < 0) - return -errno; - } - - /* Check timestamp of interval file to see whether cleaning is necessary. */ - time_t clean_interval; - interval_file = fopen(interval_path, "r"); - if (fscanf(interval_file, "%ld", &clean_interval) != 1) - clean_interval = cache_clean_default_interval_s; - fclose(interval_file); - - if (time(NULL) - st.st_mtime < clean_interval) - /* Interval has not passed, skip cleaning. */ - return 0; - - /* Read max unused age value from config file. */ - time_t max_unused_age; - max_unused_file = fopen(max_unused_path, "r"); - if (max_unused_file) - { - if (fscanf(max_unused_file, "%ld", &max_unused_age) != 1) - max_unused_age = cache_default_max_unused_age_s; - fclose(max_unused_file); - } - else - max_unused_age = cache_default_max_unused_age_s; - - char * const dirs[] = { cache_path, NULL, }; - - FTS *fts = fts_open(dirs, 0, NULL); - if (fts == NULL) - return -errno; - - FTSENT *f; - long files = 0; - while ((f = fts_read(fts)) != NULL) - { - files++; - if (c->progressfn) /* inform/check progress callback */ - if ((c->progressfn) (c, files, 0)) - break; - - switch (f->fts_info) - { - case FTS_F: - /* delete file if max_unused_age has been met or exceeded. */ - /* XXX consider extra effort to clean up old tmp files */ - if (time(NULL) - f->fts_statp->st_atime >= max_unused_age) - unlink (f->fts_path); - break; - - case FTS_DP: - /* Remove if empty. */ - (void) rmdir (f->fts_path); - break; - - default: - ; - } - } - fts_close(fts); - - /* Update timestamp representing when the cache was last cleaned. */ - utime (interval_path, NULL); - return 0; -} - - -#define MAX_BUILD_ID_BYTES 64 - - -/* Query each of the server URLs found in $DEBUGINFOD_URLS for the file - with the specified build-id, type (debuginfo, executable or source) - and filename. filename may be NULL. If found, return a file - descriptor for the target, otherwise return an error code. -*/ -static int -debuginfod_query_server (debuginfod_client *c, - const unsigned char *build_id, - int build_id_len, - const char *type, - const char *filename, - char **path) -{ - char *urls_envvar; - char *server_urls; - char cache_path[PATH_MAX]; - char maxage_path[PATH_MAX*3]; /* These *3 multipliers are to shut up gcc -Wformat-truncation */ - char interval_path[PATH_MAX*4]; - char target_cache_dir[PATH_MAX*2]; - char target_cache_path[PATH_MAX*4]; - char target_cache_tmppath[PATH_MAX*5]; - char suffix[PATH_MAX*2]; - char build_id_bytes[MAX_BUILD_ID_BYTES * 2 + 1]; - int rc; - - /* Is there any server we can query? If not, don't do any work, - just return with ENOSYS. Don't even access the cache. */ - urls_envvar = getenv(server_urls_envvar); - if (urls_envvar == NULL || urls_envvar[0] == '\0') - { - rc = -ENOSYS; - goto out; - } - - /* Copy lowercase hex representation of build_id into buf. */ - if ((build_id_len >= MAX_BUILD_ID_BYTES) || - (build_id_len == 0 && - sizeof(build_id_bytes) > MAX_BUILD_ID_BYTES*2 + 1)) - return -EINVAL; - if (build_id_len == 0) /* expect clean hexadecimal */ - strcpy (build_id_bytes, (const char *) build_id); - else - for (int i = 0; i < build_id_len; i++) - sprintf(build_id_bytes + (i * 2), "%02x", build_id[i]); - - if (filename != NULL) - { - if (filename[0] != '/') // must start with / - return -EINVAL; - - /* copy the filename to suffix, s,/,#,g */ - unsigned q = 0; - for (unsigned fi=0; q < PATH_MAX-1; fi++) - switch (filename[fi]) - { - case '\0': - suffix[q] = '\0'; - q = PATH_MAX-1; /* escape for loop too */ - break; - case '/': /* escape / to prevent dir escape */ - suffix[q++]='#'; - suffix[q++]='#'; - break; - case '#': /* escape # to prevent /# vs #/ collisions */ - suffix[q++]='#'; - suffix[q++]='_'; - break; - default: - suffix[q++]=filename[fi]; - } - suffix[q] = '\0'; - /* If the DWARF filenames are super long, this could exceed - PATH_MAX and truncate/collide. Oh well, that'll teach - them! */ - } - else - suffix[0] = '\0'; - - /* set paths needed to perform the query - - example format - cache_path: $HOME/.debuginfod_cache - target_cache_dir: $HOME/.debuginfod_cache/0123abcd - target_cache_path: $HOME/.debuginfod_cache/0123abcd/debuginfo - target_cache_path: $HOME/.debuginfod_cache/0123abcd/source#PATH#TO#SOURCE ? - */ - - if (getenv(cache_path_envvar)) - strcpy(cache_path, getenv(cache_path_envvar)); - else - { - if (getenv("HOME")) - sprintf(cache_path, "%s/%s", getenv("HOME"), cache_default_name); - else - sprintf(cache_path, "/%s", cache_default_name); - } - - /* avoid using snprintf here due to compiler warning. */ - snprintf(target_cache_dir, sizeof(target_cache_dir), "%s/%s", cache_path, build_id_bytes); - snprintf(target_cache_path, sizeof(target_cache_path), "%s/%s%s", target_cache_dir, type, suffix); - snprintf(target_cache_tmppath, sizeof(target_cache_tmppath), "%s.XXXXXX", target_cache_path); - - /* XXX combine these */ - snprintf(interval_path, sizeof(interval_path), "%s/%s", cache_path, cache_clean_interval_filename); - snprintf(maxage_path, sizeof(maxage_path), "%s/%s", cache_path, cache_max_unused_age_filename); - rc = debuginfod_init_cache(cache_path, interval_path, maxage_path); - if (rc != 0) - goto out; - rc = debuginfod_clean_cache(c, cache_path, interval_path, maxage_path); - if (rc != 0) - goto out; - - /* If the target is already in the cache then we are done. */ - int fd = open (target_cache_path, O_RDONLY); - if (fd >= 0) - { - /* Success!!!! */ - if (path != NULL) - *path = strdup(target_cache_path); - return fd; - } - - if (getenv(server_timeout_envvar)) - server_timeout = atoi (getenv(server_timeout_envvar)); - - /* make a copy of the envvar so it can be safely modified. */ - server_urls = strdup(urls_envvar); - if (server_urls == NULL) - { - rc = -ENOMEM; - goto out; - } - /* thereafter, goto out0 on error*/ - - /* create target directory in cache if not found. */ - struct stat st; - if (stat(target_cache_dir, &st) == -1 && mkdir(target_cache_dir, 0700) < 0) - { - rc = -errno; - goto out0; - } - - /* NB: write to a temporary file first, to avoid race condition of - multiple clients checking the cache, while a partially-written or empty - file is in there, being written from libcurl. */ - fd = mkstemp (target_cache_tmppath); - if (fd < 0) - { - rc = -errno; - goto out0; - } - - /* Count number of URLs. */ - int num_urls = 0; - for (int i = 0; server_urls[i] != '\0'; i++) - if (server_urls[i] != url_delim_char - && (i == 0 || server_urls[i - 1] == url_delim_char)) - num_urls++; - - /* Tracks which handle should write to fd. Set to the first - handle that is ready to write the target file to the cache. */ - CURL *target_handle = NULL; - struct handle_data *data = malloc(sizeof(struct handle_data) * num_urls); - - /* Initalize handle_data with default values. */ - for (int i = 0; i < num_urls; i++) - { - data[i].handle = NULL; - data[i].fd = -1; - } - - CURLM *curlm = curl_multi_init(); - if (curlm == NULL) - { - rc = -ENETUNREACH; - goto out0; - } - /* thereafter, goto out1 on error. */ - - char *strtok_saveptr; - char *server_url = strtok_r(server_urls, url_delim, &strtok_saveptr); - - /* Initialize each handle. */ - for (int i = 0; i < num_urls && server_url != NULL; i++) - { - data[i].fd = fd; - data[i].target_handle = &target_handle; - data[i].handle = curl_easy_init(); - - if (data[i].handle == NULL) - { - rc = -ENETUNREACH; - goto out1; - } - - /* Build handle url. Tolerate both http://foo:999 and - http://foo:999/ forms */ - char *slashbuildid; - if (strlen(server_url) > 1 && server_url[strlen(server_url)-1] == '/') - slashbuildid = "buildid"; - else - slashbuildid = "/buildid"; - - if (filename) /* must start with / */ - snprintf(data[i].url, PATH_MAX, "%s%s/%s/%s%s", server_url, - slashbuildid, build_id_bytes, type, filename); - else - snprintf(data[i].url, PATH_MAX, "%s%s/%s/%s", server_url, - slashbuildid, build_id_bytes, type); - - curl_easy_setopt(data[i].handle, CURLOPT_URL, data[i].url); - curl_easy_setopt(data[i].handle, - CURLOPT_WRITEFUNCTION, - debuginfod_write_callback); - curl_easy_setopt(data[i].handle, CURLOPT_WRITEDATA, (void*)&data[i]); - curl_easy_setopt(data[i].handle, CURLOPT_TIMEOUT, (long) server_timeout); - curl_easy_setopt(data[i].handle, CURLOPT_FILETIME, (long) 1); - curl_easy_setopt(data[i].handle, CURLOPT_FOLLOWLOCATION, (long) 1); - curl_easy_setopt(data[i].handle, CURLOPT_FAILONERROR, (long) 1); - curl_easy_setopt(data[i].handle, CURLOPT_NOSIGNAL, (long) 1); - curl_easy_setopt(data[i].handle, CURLOPT_AUTOREFERER, (long) 1); - curl_easy_setopt(data[i].handle, CURLOPT_ACCEPT_ENCODING, ""); - curl_easy_setopt(data[i].handle, CURLOPT_USERAGENT, (void*) PACKAGE_STRING); - - curl_multi_add_handle(curlm, data[i].handle); - server_url = strtok_r(NULL, url_delim, &strtok_saveptr); - } - - /* Query servers in parallel. */ - int still_running; - long loops = 0; - do - { - CURLMcode curl_res; - - if (c->progressfn) /* inform/check progress callback */ - { - loops ++; - long pa = loops; /* default params for progress callback */ - long pb = 0; - if (target_handle) /* we've committed to a server; report its download progress */ - { -#ifdef CURLINFO_SIZE_DOWNLOAD_T - curl_off_t dl; - curl_res = curl_easy_getinfo(target_handle, - CURLINFO_SIZE_DOWNLOAD_T, - &dl); - if (curl_res == 0 && dl >= 0) - pa = (dl > LONG_MAX ? LONG_MAX : (long)dl); -#else - double dl; - curl_res = curl_easy_getinfo(target_handle, - CURLINFO_SIZE_DOWNLOAD, - &dl); - if (curl_res == 0) - pa = (dl > LONG_MAX ? LONG_MAX : (long)dl); -#endif - -#ifdef CURLINFO_CURLINFO_CONTENT_LENGTH_DOWNLOAD_T - curl_off_t cl; - curl_res = curl_easy_getinfo(target_handle, - CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, - &cl); - if (curl_res == 0 && cl >= 0) - pb = (cl > LONG_MAX ? LONG_MAX : (long)cl); -#else - double cl; - curl_res = curl_easy_getinfo(target_handle, - CURLINFO_CONTENT_LENGTH_DOWNLOAD, - &cl); - if (curl_res == 0) - pb = (cl > LONG_MAX ? LONG_MAX : (long)cl); -#endif - } - - if ((*c->progressfn) (c, pa, pb)) - break; - } - - /* Wait 1 second, the minimum DEBUGINFOD_TIMEOUT. */ - curl_multi_wait(curlm, NULL, 0, 1000, NULL); - - /* If the target file has been found, abort the other queries. */ - if (target_handle != NULL) - for (int i = 0; i < num_urls; i++) - if (data[i].handle != target_handle) - curl_multi_remove_handle(curlm, data[i].handle); - - curl_res = curl_multi_perform(curlm, &still_running); - if (curl_res != CURLM_OK) - { - switch (curl_res) - { - case CURLM_CALL_MULTI_PERFORM: continue; - case CURLM_OUT_OF_MEMORY: rc = -ENOMEM; break; - default: rc = -ENETUNREACH; break; - } - goto out1; - } - } while (still_running); - - /* Check whether a query was successful. If so, assign its handle - to verified_handle. */ - int num_msg; - rc = -ENOENT; - CURL *verified_handle = NULL; - do - { - CURLMsg *msg; - - msg = curl_multi_info_read(curlm, &num_msg); - if (msg != NULL && msg->msg == CURLMSG_DONE) - { - if (msg->data.result != CURLE_OK) - { - /* Unsucessful query, determine error code. */ - switch (msg->data.result) - { - case CURLE_COULDNT_RESOLVE_HOST: rc = -EHOSTUNREACH; break; // no NXDOMAIN - case CURLE_URL_MALFORMAT: rc = -EINVAL; break; - case CURLE_COULDNT_CONNECT: rc = -ECONNREFUSED; break; - case CURLE_REMOTE_ACCESS_DENIED: rc = -EACCES; break; - case CURLE_WRITE_ERROR: rc = -EIO; break; - case CURLE_OUT_OF_MEMORY: rc = -ENOMEM; break; - case CURLE_TOO_MANY_REDIRECTS: rc = -EMLINK; break; - case CURLE_SEND_ERROR: rc = -ECONNRESET; break; - case CURLE_RECV_ERROR: rc = -ECONNRESET; break; - case CURLE_OPERATION_TIMEDOUT: rc = -ETIME; break; - default: rc = -ENOENT; break; - } - } - else - { - /* Query completed without an error. Confirm that the - response code is 200 and set verified_handle. */ - long resp_code = 500; - CURLcode curl_res; - - curl_res = curl_easy_getinfo(target_handle, - CURLINFO_RESPONSE_CODE, - &resp_code); - - if (curl_res == CURLE_OK - && resp_code == 200 - && msg->easy_handle != NULL) - { - verified_handle = msg->easy_handle; - break; - } - } - } - } while (num_msg > 0); - - if (verified_handle == NULL) - goto out1; - - /* we've got one!!!! */ - time_t mtime; - CURLcode curl_res = curl_easy_getinfo(verified_handle, CURLINFO_FILETIME, (void*) &mtime); - if (curl_res != CURLE_OK) - mtime = time(NULL); /* fall back to current time */ - - struct timeval tvs[2]; - tvs[0].tv_sec = tvs[1].tv_sec = mtime; - tvs[0].tv_usec = tvs[1].tv_usec = 0; - (void) futimes (fd, tvs); /* best effort */ - - /* rename tmp->real */ - rc = rename (target_cache_tmppath, target_cache_path); - if (rc < 0) - { - rc = -errno; - goto out1; - /* Perhaps we need not give up right away; could retry or something ... */ - } - - /* Success!!!! */ - for (int i = 0; i < num_urls; i++) - curl_easy_cleanup(data[i].handle); - - curl_multi_cleanup (curlm); - free (data); - free (server_urls); - /* don't close fd - we're returning it */ - /* don't unlink the tmppath; it's already been renamed. */ - if (path != NULL) - *path = strdup(target_cache_path); - - return fd; - -/* error exits */ - out1: - for (int i = 0; i < num_urls; i++) - curl_easy_cleanup(data[i].handle); - - curl_multi_cleanup(curlm); - unlink (target_cache_tmppath); - (void) rmdir (target_cache_dir); /* nop if not empty */ - free(data); - close (fd); - - out0: - free (server_urls); - - out: - return rc; -} - -/* See debuginfod.h */ -debuginfod_client * -debuginfod_begin (void) -{ - debuginfod_client *client; - size_t size = sizeof (struct debuginfod_client); - client = (debuginfod_client *) malloc (size); - if (client != NULL) - client->progressfn = NULL; - return client; -} - -void -debuginfod_end (debuginfod_client *client) -{ - free (client); -} - -int -debuginfod_find_debuginfo (debuginfod_client *client, - const unsigned char *build_id, int build_id_len, - char **path) -{ - return debuginfod_query_server(client, build_id, build_id_len, - "debuginfo", NULL, path); -} - - -/* See debuginfod.h */ -int -debuginfod_find_executable(debuginfod_client *client, - const unsigned char *build_id, int build_id_len, - char **path) -{ - return debuginfod_query_server(client, build_id, build_id_len, - "executable", NULL, path); -} - -/* See debuginfod.h */ -int debuginfod_find_source(debuginfod_client *client, - const unsigned char *build_id, int build_id_len, - const char *filename, char **path) -{ - return debuginfod_query_server(client, build_id, build_id_len, - "source", filename, path); -} - - -void -debuginfod_set_progressfn(debuginfod_client *client, - debuginfod_progressfn_t fn) -{ - client->progressfn = fn; -} - - -/* NB: these are thread-unsafe. */ -__attribute__((constructor)) attribute_hidden void libdebuginfod_ctor(void) -{ - curl_global_init(CURL_GLOBAL_DEFAULT); -} - -/* NB: this is very thread-unsafe: it breaks other threads that are still in libcurl */ -__attribute__((destructor)) attribute_hidden void libdebuginfod_dtor(void) -{ - /* ... so don't do this: */ - /* curl_global_cleanup(); */ -} diff --git a/debuginfod/debuginfod-client.c.debuginfod-client-cache b/debuginfod/debuginfod-client.c.debuginfod-client-cache deleted file mode 100644 index 6e62b86..0000000 --- a/debuginfod/debuginfod-client.c.debuginfod-client-cache +++ /dev/null @@ -1,754 +0,0 @@ -/* Retrieve ELF / DWARF / source files from the debuginfod. - Copyright (C) 2019 Red Hat, Inc. - This file is part of elfutils. - - This file is free software; you can redistribute it and/or modify - it under the terms of either - - * the GNU Lesser General Public License as published by the Free - Software Foundation; either version 3 of the License, or (at - your option) any later version - - or - - * the GNU General Public License as published by the Free - Software Foundation; either version 2 of the License, or (at - your option) any later version - - or both in parallel, as here. - - elfutils 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 copies of the GNU General Public License and - the GNU Lesser General Public License along with this program. If - not, see . */ - - -/* cargo-cult from libdwfl linux-kernel-modules.c */ -/* In case we have a bad fts we include this before config.h because it - can't handle _FILE_OFFSET_BITS. - Everything we need here is fine if its declarations just come first. - Also, include sys/types.h before fts. On some systems fts.h is not self - contained. */ -#ifdef BAD_FTS - #include - #include -#endif - -#include "config.h" -#include "debuginfod.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* If fts.h is included before config.h, its indirect inclusions may not - give us the right LFS aliases of these functions, so map them manually. */ -#ifdef BAD_FTS - #ifdef _FILE_OFFSET_BITS - #define open open64 - #define fopen fopen64 - #endif -#else - #include - #include -#endif - -struct debuginfod_client -{ - /* Progress/interrupt callback function. */ - debuginfod_progressfn_t progressfn; - - /* Can contain all other context, like cache_path, server_urls, - timeout or other info gotten from environment variables, the - handle data, etc. So those don't have to be reparsed and - recreated on each request. */ -}; - -/* The cache_clean_interval_s file within the debuginfod cache specifies - how frequently the cache should be cleaned. The file's st_mtime represents - the time of last cleaning. */ -static const char *cache_clean_interval_filename = "cache_clean_interval_s"; -static const time_t cache_clean_default_interval_s = 86400; /* 1 day */ - -/* The cache_max_unused_age_s file within the debuginfod cache specifies the - the maximum time since last access that a file will remain in the cache. */ -static const char *cache_max_unused_age_filename = "max_unused_age_s"; -static const time_t cache_default_max_unused_age_s = 604800; /* 1 week */ - -/* Location of the cache of files downloaded from debuginfods. - The default parent directory is $HOME, or '/' if $HOME doesn't exist. */ -static const char *cache_default_name = ".debuginfod_client_cache"; -static const char *cache_path_envvar = DEBUGINFOD_CACHE_PATH_ENV_VAR; - -/* URLs of debuginfods, separated by url_delim. - This env var must be set for debuginfod-client to run. */ -static const char *server_urls_envvar = DEBUGINFOD_URLS_ENV_VAR; -static const char *url_delim = " "; -static const char url_delim_char = ' '; - -/* Timeout for debuginfods, in seconds. - This env var must be set for debuginfod-client to run. */ -static const char *server_timeout_envvar = DEBUGINFOD_TIMEOUT_ENV_VAR; -static int server_timeout = 5; - -/* Data associated with a particular CURL easy handle. Passed to - the write callback. */ -struct handle_data -{ - /* Cache file to be written to in case query is successful. */ - int fd; - - /* URL queried by this handle. */ - char url[PATH_MAX]; - - /* This handle. */ - CURL *handle; - - /* Pointer to handle that should write to fd. Initially points to NULL, - then points to the first handle that begins writing the target file - to the cache. Used to ensure that a file is not downloaded from - multiple servers unnecessarily. */ - CURL **target_handle; -}; - -static size_t -debuginfod_write_callback (char *ptr, size_t size, size_t nmemb, void *data) -{ - ssize_t count = size * nmemb; - - struct handle_data *d = (struct handle_data*)data; - - /* Indicate to other handles that they can abort their transfer. */ - if (*d->target_handle == NULL) - *d->target_handle = d->handle; - - /* If this handle isn't the target handle, abort transfer. */ - if (*d->target_handle != d->handle) - return -1; - - return (size_t) write(d->fd, (void*)ptr, count); -} - -/* Create the cache and interval file if they do not already exist. - Return 0 if cache and config file are initialized, otherwise return - the appropriate error code. */ -static int -debuginfod_init_cache (char *cache_path, char *interval_path, char *maxage_path) -{ - struct stat st; - - /* If the cache and config file already exist then we are done. */ - if (stat(cache_path, &st) == 0 && stat(interval_path, &st) == 0) - return 0; - - /* Create the cache and config files as necessary. */ - if (stat(cache_path, &st) != 0 && mkdir(cache_path, 0777) < 0) - return -errno; - - int fd = -1; - - /* init cleaning interval config file. */ - fd = open(interval_path, O_CREAT | O_RDWR, 0666); - if (fd < 0) - return -errno; - - if (dprintf(fd, "%ld", cache_clean_default_interval_s) < 0) - return -errno; - - /* init max age config file. */ - if (stat(maxage_path, &st) != 0 - && (fd = open(maxage_path, O_CREAT | O_RDWR, 0666)) < 0) - return -errno; - - if (dprintf(fd, "%ld", cache_default_max_unused_age_s) < 0) - return -errno; - - return 0; -} - - -/* Delete any files that have been unmodied for a period - longer than $DEBUGINFOD_CACHE_CLEAN_INTERVAL_S. */ -static int -debuginfod_clean_cache(debuginfod_client *c, - char *cache_path, char *interval_path, - char *max_unused_path) -{ - struct stat st; - FILE *interval_file; - FILE *max_unused_file; - - if (stat(interval_path, &st) == -1) - { - /* Create new interval file. */ - interval_file = fopen(interval_path, "w"); - - if (interval_file == NULL) - return -errno; - - int rc = fprintf(interval_file, "%ld", cache_clean_default_interval_s); - fclose(interval_file); - - if (rc < 0) - return -errno; - } - - /* Check timestamp of interval file to see whether cleaning is necessary. */ - time_t clean_interval; - interval_file = fopen(interval_path, "r"); - if (fscanf(interval_file, "%ld", &clean_interval) != 1) - clean_interval = cache_clean_default_interval_s; - fclose(interval_file); - - if (time(NULL) - st.st_mtime < clean_interval) - /* Interval has not passed, skip cleaning. */ - return 0; - - /* Read max unused age value from config file. */ - time_t max_unused_age; - max_unused_file = fopen(max_unused_path, "r"); - if (max_unused_file) - { - if (fscanf(max_unused_file, "%ld", &max_unused_age) != 1) - max_unused_age = cache_default_max_unused_age_s; - fclose(max_unused_file); - } - else - max_unused_age = cache_default_max_unused_age_s; - - char * const dirs[] = { cache_path, NULL, }; - - FTS *fts = fts_open(dirs, 0, NULL); - if (fts == NULL) - return -errno; - - FTSENT *f; - long files = 0; - while ((f = fts_read(fts)) != NULL) - { - files++; - if (c->progressfn) /* inform/check progress callback */ - if ((c->progressfn) (c, files, 0)) - break; - - switch (f->fts_info) - { - case FTS_F: - /* delete file if max_unused_age has been met or exceeded. */ - /* XXX consider extra effort to clean up old tmp files */ - if (time(NULL) - f->fts_statp->st_atime >= max_unused_age) - unlink (f->fts_path); - break; - - case FTS_DP: - /* Remove if empty. */ - (void) rmdir (f->fts_path); - break; - - default: - ; - } - } - fts_close(fts); - - /* Update timestamp representing when the cache was last cleaned. */ - utime (interval_path, NULL); - return 0; -} - - -#define MAX_BUILD_ID_BYTES 64 - - -/* Query each of the server URLs found in $DEBUGINFOD_URLS for the file - with the specified build-id, type (debuginfo, executable or source) - and filename. filename may be NULL. If found, return a file - descriptor for the target, otherwise return an error code. -*/ -static int -debuginfod_query_server (debuginfod_client *c, - const unsigned char *build_id, - int build_id_len, - const char *type, - const char *filename, - char **path) -{ - char *urls_envvar; - char *server_urls; - char cache_path[PATH_MAX]; - char maxage_path[PATH_MAX*3]; /* These *3 multipliers are to shut up gcc -Wformat-truncation */ - char interval_path[PATH_MAX*4]; - char target_cache_dir[PATH_MAX*2]; - char target_cache_path[PATH_MAX*4]; - char target_cache_tmppath[PATH_MAX*5]; - char suffix[PATH_MAX*2]; - char build_id_bytes[MAX_BUILD_ID_BYTES * 2 + 1]; - - /* Copy lowercase hex representation of build_id into buf. */ - if ((build_id_len >= MAX_BUILD_ID_BYTES) || - (build_id_len == 0 && - sizeof(build_id_bytes) > MAX_BUILD_ID_BYTES*2 + 1)) - return -EINVAL; - if (build_id_len == 0) /* expect clean hexadecimal */ - strcpy (build_id_bytes, (const char *) build_id); - else - for (int i = 0; i < build_id_len; i++) - sprintf(build_id_bytes + (i * 2), "%02x", build_id[i]); - - if (filename != NULL) - { - if (filename[0] != '/') // must start with / - return -EINVAL; - - /* copy the filename to suffix, s,/,#,g */ - unsigned q = 0; - for (unsigned fi=0; q < PATH_MAX-1; fi++) - switch (filename[fi]) - { - case '\0': - suffix[q] = '\0'; - q = PATH_MAX-1; /* escape for loop too */ - break; - case '/': /* escape / to prevent dir escape */ - suffix[q++]='#'; - suffix[q++]='#'; - break; - case '#': /* escape # to prevent /# vs #/ collisions */ - suffix[q++]='#'; - suffix[q++]='_'; - break; - default: - suffix[q++]=filename[fi]; - } - suffix[q] = '\0'; - /* If the DWARF filenames are super long, this could exceed - PATH_MAX and truncate/collide. Oh well, that'll teach - them! */ - } - else - suffix[0] = '\0'; - - /* set paths needed to perform the query - - example format - cache_path: $HOME/.debuginfod_cache - target_cache_dir: $HOME/.debuginfod_cache/0123abcd - target_cache_path: $HOME/.debuginfod_cache/0123abcd/debuginfo - target_cache_path: $HOME/.debuginfod_cache/0123abcd/source#PATH#TO#SOURCE ? - */ - - if (getenv(cache_path_envvar)) - strcpy(cache_path, getenv(cache_path_envvar)); - else - { - if (getenv("HOME")) - sprintf(cache_path, "%s/%s", getenv("HOME"), cache_default_name); - else - sprintf(cache_path, "/%s", cache_default_name); - } - - /* avoid using snprintf here due to compiler warning. */ - snprintf(target_cache_dir, sizeof(target_cache_dir), "%s/%s", cache_path, build_id_bytes); - snprintf(target_cache_path, sizeof(target_cache_path), "%s/%s%s", target_cache_dir, type, suffix); - snprintf(target_cache_tmppath, sizeof(target_cache_tmppath), "%s.XXXXXX", target_cache_path); - - /* XXX combine these */ - snprintf(interval_path, sizeof(interval_path), "%s/%s", cache_path, cache_clean_interval_filename); - snprintf(maxage_path, sizeof(maxage_path), "%s/%s", cache_path, cache_max_unused_age_filename); - int rc = debuginfod_init_cache(cache_path, interval_path, maxage_path); - if (rc != 0) - goto out; - rc = debuginfod_clean_cache(c, cache_path, interval_path, maxage_path); - if (rc != 0) - goto out; - - /* If the target is already in the cache then we are done. */ - int fd = open (target_cache_path, O_RDONLY); - if (fd >= 0) - { - /* Success!!!! */ - if (path != NULL) - *path = strdup(target_cache_path); - return fd; - } - - - urls_envvar = getenv(server_urls_envvar); - if (urls_envvar == NULL || urls_envvar[0] == '\0') - { - rc = -ENOSYS; - goto out; - } - - if (getenv(server_timeout_envvar)) - server_timeout = atoi (getenv(server_timeout_envvar)); - - /* make a copy of the envvar so it can be safely modified. */ - server_urls = strdup(urls_envvar); - if (server_urls == NULL) - { - rc = -ENOMEM; - goto out; - } - /* thereafter, goto out0 on error*/ - - /* create target directory in cache if not found. */ - struct stat st; - if (stat(target_cache_dir, &st) == -1 && mkdir(target_cache_dir, 0700) < 0) - { - rc = -errno; - goto out0; - } - - /* NB: write to a temporary file first, to avoid race condition of - multiple clients checking the cache, while a partially-written or empty - file is in there, being written from libcurl. */ - fd = mkstemp (target_cache_tmppath); - if (fd < 0) - { - rc = -errno; - goto out0; - } - - /* Count number of URLs. */ - int num_urls = 0; - for (int i = 0; server_urls[i] != '\0'; i++) - if (server_urls[i] != url_delim_char - && (i == 0 || server_urls[i - 1] == url_delim_char)) - num_urls++; - - /* Tracks which handle should write to fd. Set to the first - handle that is ready to write the target file to the cache. */ - CURL *target_handle = NULL; - struct handle_data *data = malloc(sizeof(struct handle_data) * num_urls); - - /* Initalize handle_data with default values. */ - for (int i = 0; i < num_urls; i++) - { - data[i].handle = NULL; - data[i].fd = -1; - } - - CURLM *curlm = curl_multi_init(); - if (curlm == NULL) - { - rc = -ENETUNREACH; - goto out0; - } - /* thereafter, goto out1 on error. */ - - char *strtok_saveptr; - char *server_url = strtok_r(server_urls, url_delim, &strtok_saveptr); - - /* Initialize each handle. */ - for (int i = 0; i < num_urls && server_url != NULL; i++) - { - data[i].fd = fd; - data[i].target_handle = &target_handle; - data[i].handle = curl_easy_init(); - - if (data[i].handle == NULL) - { - rc = -ENETUNREACH; - goto out1; - } - - /* Build handle url. Tolerate both http://foo:999 and - http://foo:999/ forms */ - char *slashbuildid; - if (strlen(server_url) > 1 && server_url[strlen(server_url)-1] == '/') - slashbuildid = "buildid"; - else - slashbuildid = "/buildid"; - - if (filename) /* must start with / */ - snprintf(data[i].url, PATH_MAX, "%s%s/%s/%s%s", server_url, - slashbuildid, build_id_bytes, type, filename); - else - snprintf(data[i].url, PATH_MAX, "%s%s/%s/%s", server_url, - slashbuildid, build_id_bytes, type); - - curl_easy_setopt(data[i].handle, CURLOPT_URL, data[i].url); - curl_easy_setopt(data[i].handle, - CURLOPT_WRITEFUNCTION, - debuginfod_write_callback); - curl_easy_setopt(data[i].handle, CURLOPT_WRITEDATA, (void*)&data[i]); - curl_easy_setopt(data[i].handle, CURLOPT_TIMEOUT, (long) server_timeout); - curl_easy_setopt(data[i].handle, CURLOPT_FILETIME, (long) 1); - curl_easy_setopt(data[i].handle, CURLOPT_FOLLOWLOCATION, (long) 1); - curl_easy_setopt(data[i].handle, CURLOPT_FAILONERROR, (long) 1); - curl_easy_setopt(data[i].handle, CURLOPT_NOSIGNAL, (long) 1); - curl_easy_setopt(data[i].handle, CURLOPT_AUTOREFERER, (long) 1); - curl_easy_setopt(data[i].handle, CURLOPT_ACCEPT_ENCODING, ""); - curl_easy_setopt(data[i].handle, CURLOPT_USERAGENT, (void*) PACKAGE_STRING); - - curl_multi_add_handle(curlm, data[i].handle); - server_url = strtok_r(NULL, url_delim, &strtok_saveptr); - } - - /* Query servers in parallel. */ - int still_running; - long loops = 0; - do - { - CURLMcode curl_res; - - if (c->progressfn) /* inform/check progress callback */ - { - loops ++; - long pa = loops; /* default params for progress callback */ - long pb = 0; - if (target_handle) /* we've committed to a server; report its download progress */ - { -#ifdef CURLINFO_SIZE_DOWNLOAD_T - curl_off_t dl; - curl_res = curl_easy_getinfo(target_handle, - CURLINFO_SIZE_DOWNLOAD_T, - &dl); - if (curl_res == 0 && dl >= 0) - pa = (dl > LONG_MAX ? LONG_MAX : (long)dl); -#else - double dl; - curl_res = curl_easy_getinfo(target_handle, - CURLINFO_SIZE_DOWNLOAD, - &dl); - if (curl_res == 0) - pa = (dl > LONG_MAX ? LONG_MAX : (long)dl); -#endif - -#ifdef CURLINFO_CURLINFO_CONTENT_LENGTH_DOWNLOAD_T - curl_off_t cl; - curl_res = curl_easy_getinfo(target_handle, - CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, - &cl); - if (curl_res == 0 && cl >= 0) - pb = (cl > LONG_MAX ? LONG_MAX : (long)cl); -#else - double cl; - curl_res = curl_easy_getinfo(target_handle, - CURLINFO_CONTENT_LENGTH_DOWNLOAD, - &cl); - if (curl_res == 0) - pb = (cl > LONG_MAX ? LONG_MAX : (long)cl); -#endif - } - - if ((*c->progressfn) (c, pa, pb)) - break; - } - - /* Wait 1 second, the minimum DEBUGINFOD_TIMEOUT. */ - curl_multi_wait(curlm, NULL, 0, 1000, NULL); - - /* If the target file has been found, abort the other queries. */ - if (target_handle != NULL) - for (int i = 0; i < num_urls; i++) - if (data[i].handle != target_handle) - curl_multi_remove_handle(curlm, data[i].handle); - - curl_res = curl_multi_perform(curlm, &still_running); - if (curl_res != CURLM_OK) - { - switch (curl_res) - { - case CURLM_CALL_MULTI_PERFORM: continue; - case CURLM_OUT_OF_MEMORY: rc = -ENOMEM; break; - default: rc = -ENETUNREACH; break; - } - goto out1; - } - } while (still_running); - - /* Check whether a query was successful. If so, assign its handle - to verified_handle. */ - int num_msg; - rc = -ENOENT; - CURL *verified_handle = NULL; - do - { - CURLMsg *msg; - - msg = curl_multi_info_read(curlm, &num_msg); - if (msg != NULL && msg->msg == CURLMSG_DONE) - { - if (msg->data.result != CURLE_OK) - { - /* Unsucessful query, determine error code. */ - switch (msg->data.result) - { - case CURLE_COULDNT_RESOLVE_HOST: rc = -EHOSTUNREACH; break; // no NXDOMAIN - case CURLE_URL_MALFORMAT: rc = -EINVAL; break; - case CURLE_COULDNT_CONNECT: rc = -ECONNREFUSED; break; - case CURLE_REMOTE_ACCESS_DENIED: rc = -EACCES; break; - case CURLE_WRITE_ERROR: rc = -EIO; break; - case CURLE_OUT_OF_MEMORY: rc = -ENOMEM; break; - case CURLE_TOO_MANY_REDIRECTS: rc = -EMLINK; break; - case CURLE_SEND_ERROR: rc = -ECONNRESET; break; - case CURLE_RECV_ERROR: rc = -ECONNRESET; break; - case CURLE_OPERATION_TIMEDOUT: rc = -ETIME; break; - default: rc = -ENOENT; break; - } - } - else - { - /* Query completed without an error. Confirm that the - response code is 200 and set verified_handle. */ - long resp_code = 500; - CURLcode curl_res; - - curl_res = curl_easy_getinfo(target_handle, - CURLINFO_RESPONSE_CODE, - &resp_code); - - if (curl_res == CURLE_OK - && resp_code == 200 - && msg->easy_handle != NULL) - { - verified_handle = msg->easy_handle; - break; - } - } - } - } while (num_msg > 0); - - if (verified_handle == NULL) - goto out1; - - /* we've got one!!!! */ - time_t mtime; - CURLcode curl_res = curl_easy_getinfo(verified_handle, CURLINFO_FILETIME, (void*) &mtime); - if (curl_res != CURLE_OK) - mtime = time(NULL); /* fall back to current time */ - - struct timeval tvs[2]; - tvs[0].tv_sec = tvs[1].tv_sec = mtime; - tvs[0].tv_usec = tvs[1].tv_usec = 0; - (void) futimes (fd, tvs); /* best effort */ - - /* rename tmp->real */ - rc = rename (target_cache_tmppath, target_cache_path); - if (rc < 0) - { - rc = -errno; - goto out1; - /* Perhaps we need not give up right away; could retry or something ... */ - } - - /* Success!!!! */ - for (int i = 0; i < num_urls; i++) - curl_easy_cleanup(data[i].handle); - - curl_multi_cleanup (curlm); - free (data); - free (server_urls); - /* don't close fd - we're returning it */ - /* don't unlink the tmppath; it's already been renamed. */ - if (path != NULL) - *path = strdup(target_cache_path); - - return fd; - -/* error exits */ - out1: - for (int i = 0; i < num_urls; i++) - curl_easy_cleanup(data[i].handle); - - curl_multi_cleanup(curlm); - unlink (target_cache_tmppath); - (void) rmdir (target_cache_dir); /* nop if not empty */ - free(data); - close (fd); - - out0: - free (server_urls); - - out: - return rc; -} - -/* See debuginfod.h */ -debuginfod_client * -debuginfod_begin (void) -{ - debuginfod_client *client; - size_t size = sizeof (struct debuginfod_client); - client = (debuginfod_client *) malloc (size); - if (client != NULL) - client->progressfn = NULL; - return client; -} - -void -debuginfod_end (debuginfod_client *client) -{ - free (client); -} - -int -debuginfod_find_debuginfo (debuginfod_client *client, - const unsigned char *build_id, int build_id_len, - char **path) -{ - return debuginfod_query_server(client, build_id, build_id_len, - "debuginfo", NULL, path); -} - - -/* See debuginfod.h */ -int -debuginfod_find_executable(debuginfod_client *client, - const unsigned char *build_id, int build_id_len, - char **path) -{ - return debuginfod_query_server(client, build_id, build_id_len, - "executable", NULL, path); -} - -/* See debuginfod.h */ -int debuginfod_find_source(debuginfod_client *client, - const unsigned char *build_id, int build_id_len, - const char *filename, char **path) -{ - return debuginfod_query_server(client, build_id, build_id_len, - "source", filename, path); -} - - -void -debuginfod_set_progressfn(debuginfod_client *client, - debuginfod_progressfn_t fn) -{ - client->progressfn = fn; -} - - -/* NB: these are thread-unsafe. */ -__attribute__((constructor)) attribute_hidden void libdebuginfod_ctor(void) -{ - curl_global_init(CURL_GLOBAL_DEFAULT); -} - -/* NB: this is very thread-unsafe: it breaks other threads that are still in libcurl */ -__attribute__((destructor)) attribute_hidden void libdebuginfod_dtor(void) -{ - /* ... so don't do this: */ - /* curl_global_cleanup(); */ -} diff --git a/debuginfod/debuginfod-client.c.debuginfod-timeout-progress b/debuginfod/debuginfod-client.c.debuginfod-timeout-progress deleted file mode 100644 index ab7b4e1..0000000 --- a/debuginfod/debuginfod-client.c.debuginfod-timeout-progress +++ /dev/null @@ -1,755 +0,0 @@ -/* Retrieve ELF / DWARF / source files from the debuginfod. - Copyright (C) 2019 Red Hat, Inc. - This file is part of elfutils. - - This file is free software; you can redistribute it and/or modify - it under the terms of either - - * the GNU Lesser General Public License as published by the Free - Software Foundation; either version 3 of the License, or (at - your option) any later version - - or - - * the GNU General Public License as published by the Free - Software Foundation; either version 2 of the License, or (at - your option) any later version - - or both in parallel, as here. - - elfutils 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 copies of the GNU General Public License and - the GNU Lesser General Public License along with this program. If - not, see . */ - - -/* cargo-cult from libdwfl linux-kernel-modules.c */ -/* In case we have a bad fts we include this before config.h because it - can't handle _FILE_OFFSET_BITS. - Everything we need here is fine if its declarations just come first. - Also, include sys/types.h before fts. On some systems fts.h is not self - contained. */ -#ifdef BAD_FTS - #include - #include -#endif - -#include "config.h" -#include "debuginfod.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* If fts.h is included before config.h, its indirect inclusions may not - give us the right LFS aliases of these functions, so map them manually. */ -#ifdef BAD_FTS - #ifdef _FILE_OFFSET_BITS - #define open open64 - #define fopen fopen64 - #endif -#else - #include - #include -#endif - -struct debuginfod_client -{ - /* Progress/interrupt callback function. */ - debuginfod_progressfn_t progressfn; - - /* Can contain all other context, like cache_path, server_urls, - timeout or other info gotten from environment variables, the - handle data, etc. So those don't have to be reparsed and - recreated on each request. */ -}; - -/* The cache_clean_interval_s file within the debuginfod cache specifies - how frequently the cache should be cleaned. The file's st_mtime represents - the time of last cleaning. */ -static const char *cache_clean_interval_filename = "cache_clean_interval_s"; -static const time_t cache_clean_default_interval_s = 86400; /* 1 day */ - -/* The cache_max_unused_age_s file within the debuginfod cache specifies the - the maximum time since last access that a file will remain in the cache. */ -static const char *cache_max_unused_age_filename = "max_unused_age_s"; -static const time_t cache_default_max_unused_age_s = 604800; /* 1 week */ - -/* Location of the cache of files downloaded from debuginfods. - The default parent directory is $HOME, or '/' if $HOME doesn't exist. */ -static const char *cache_default_name = ".debuginfod_client_cache"; -static const char *cache_path_envvar = DEBUGINFOD_CACHE_PATH_ENV_VAR; - -/* URLs of debuginfods, separated by url_delim. - This env var must be set for debuginfod-client to run. */ -static const char *server_urls_envvar = DEBUGINFOD_URLS_ENV_VAR; -static const char *url_delim = " "; -static const char url_delim_char = ' '; - -/* Timeout for debuginfods, in seconds. - This env var must be set for debuginfod-client to run. */ -static const char *server_timeout_envvar = DEBUGINFOD_TIMEOUT_ENV_VAR; -static int server_timeout = 5; - -/* Data associated with a particular CURL easy handle. Passed to - the write callback. */ -struct handle_data -{ - /* Cache file to be written to in case query is successful. */ - int fd; - - /* URL queried by this handle. */ - char url[PATH_MAX]; - - /* This handle. */ - CURL *handle; - - /* Pointer to handle that should write to fd. Initially points to NULL, - then points to the first handle that begins writing the target file - to the cache. Used to ensure that a file is not downloaded from - multiple servers unnecessarily. */ - CURL **target_handle; -}; - -static size_t -debuginfod_write_callback (char *ptr, size_t size, size_t nmemb, void *data) -{ - ssize_t count = size * nmemb; - - struct handle_data *d = (struct handle_data*)data; - - /* Indicate to other handles that they can abort their transfer. */ - if (*d->target_handle == NULL) - *d->target_handle = d->handle; - - /* If this handle isn't the target handle, abort transfer. */ - if (*d->target_handle != d->handle) - return -1; - - return (size_t) write(d->fd, (void*)ptr, count); -} - -/* Create the cache and interval file if they do not already exist. - Return 0 if cache and config file are initialized, otherwise return - the appropriate error code. */ -static int -debuginfod_init_cache (char *cache_path, char *interval_path, char *maxage_path) -{ - struct stat st; - - /* If the cache and config file already exist then we are done. */ - if (stat(cache_path, &st) == 0 && stat(interval_path, &st) == 0) - return 0; - - /* Create the cache and config files as necessary. */ - if (stat(cache_path, &st) != 0 && mkdir(cache_path, 0777) < 0) - return -errno; - - int fd = -1; - - /* init cleaning interval config file. */ - fd = open(interval_path, O_CREAT | O_RDWR, 0666); - if (fd < 0) - return -errno; - - if (dprintf(fd, "%ld", cache_clean_default_interval_s) < 0) - return -errno; - - /* init max age config file. */ - if (stat(maxage_path, &st) != 0 - && (fd = open(maxage_path, O_CREAT | O_RDWR, 0666)) < 0) - return -errno; - - if (dprintf(fd, "%ld", cache_default_max_unused_age_s) < 0) - return -errno; - - return 0; -} - - -/* Delete any files that have been unmodied for a period - longer than $DEBUGINFOD_CACHE_CLEAN_INTERVAL_S. */ -static int -debuginfod_clean_cache(debuginfod_client *c, - char *cache_path, char *interval_path, - char *max_unused_path) -{ - struct stat st; - FILE *interval_file; - FILE *max_unused_file; - - if (stat(interval_path, &st) == -1) - { - /* Create new interval file. */ - interval_file = fopen(interval_path, "w"); - - if (interval_file == NULL) - return -errno; - - int rc = fprintf(interval_file, "%ld", cache_clean_default_interval_s); - fclose(interval_file); - - if (rc < 0) - return -errno; - } - - /* Check timestamp of interval file to see whether cleaning is necessary. */ - time_t clean_interval; - interval_file = fopen(interval_path, "r"); - if (fscanf(interval_file, "%ld", &clean_interval) != 1) - clean_interval = cache_clean_default_interval_s; - fclose(interval_file); - - if (time(NULL) - st.st_mtime < clean_interval) - /* Interval has not passed, skip cleaning. */ - return 0; - - /* Read max unused age value from config file. */ - time_t max_unused_age; - max_unused_file = fopen(max_unused_path, "r"); - if (max_unused_file) - { - if (fscanf(max_unused_file, "%ld", &max_unused_age) != 1) - max_unused_age = cache_default_max_unused_age_s; - fclose(max_unused_file); - } - else - max_unused_age = cache_default_max_unused_age_s; - - char * const dirs[] = { cache_path, NULL, }; - - FTS *fts = fts_open(dirs, 0, NULL); - if (fts == NULL) - return -errno; - - FTSENT *f; - long files = 0; - while ((f = fts_read(fts)) != NULL) - { - files++; - if (c->progressfn) /* inform/check progress callback */ - if ((c->progressfn) (c, files, 0)) - break; - - switch (f->fts_info) - { - case FTS_F: - /* delete file if max_unused_age has been met or exceeded. */ - /* XXX consider extra effort to clean up old tmp files */ - if (time(NULL) - f->fts_statp->st_atime >= max_unused_age) - unlink (f->fts_path); - break; - - case FTS_DP: - /* Remove if empty. */ - (void) rmdir (f->fts_path); - break; - - default: - ; - } - } - fts_close(fts); - - /* Update timestamp representing when the cache was last cleaned. */ - utime (interval_path, NULL); - return 0; -} - - -#define MAX_BUILD_ID_BYTES 64 - - -/* Query each of the server URLs found in $DEBUGINFOD_URLS for the file - with the specified build-id, type (debuginfo, executable or source) - and filename. filename may be NULL. If found, return a file - descriptor for the target, otherwise return an error code. -*/ -static int -debuginfod_query_server (debuginfod_client *c, - const unsigned char *build_id, - int build_id_len, - const char *type, - const char *filename, - char **path) -{ - char *urls_envvar; - char *server_urls; - char cache_path[PATH_MAX]; - char maxage_path[PATH_MAX*3]; /* These *3 multipliers are to shut up gcc -Wformat-truncation */ - char interval_path[PATH_MAX*4]; - char target_cache_dir[PATH_MAX*2]; - char target_cache_path[PATH_MAX*4]; - char target_cache_tmppath[PATH_MAX*5]; - char suffix[PATH_MAX*2]; - char build_id_bytes[MAX_BUILD_ID_BYTES * 2 + 1]; - int rc; - - /* Is there any server we can query? If not, don't do any work, - just return with ENOSYS. Don't even access the cache. */ - urls_envvar = getenv(server_urls_envvar); - if (urls_envvar == NULL || urls_envvar[0] == '\0') - { - rc = -ENOSYS; - goto out; - } - - /* Copy lowercase hex representation of build_id into buf. */ - if ((build_id_len >= MAX_BUILD_ID_BYTES) || - (build_id_len == 0 && - sizeof(build_id_bytes) > MAX_BUILD_ID_BYTES*2 + 1)) - return -EINVAL; - if (build_id_len == 0) /* expect clean hexadecimal */ - strcpy (build_id_bytes, (const char *) build_id); - else - for (int i = 0; i < build_id_len; i++) - sprintf(build_id_bytes + (i * 2), "%02x", build_id[i]); - - if (filename != NULL) - { - if (filename[0] != '/') // must start with / - return -EINVAL; - - /* copy the filename to suffix, s,/,#,g */ - unsigned q = 0; - for (unsigned fi=0; q < PATH_MAX-1; fi++) - switch (filename[fi]) - { - case '\0': - suffix[q] = '\0'; - q = PATH_MAX-1; /* escape for loop too */ - break; - case '/': /* escape / to prevent dir escape */ - suffix[q++]='#'; - suffix[q++]='#'; - break; - case '#': /* escape # to prevent /# vs #/ collisions */ - suffix[q++]='#'; - suffix[q++]='_'; - break; - default: - suffix[q++]=filename[fi]; - } - suffix[q] = '\0'; - /* If the DWARF filenames are super long, this could exceed - PATH_MAX and truncate/collide. Oh well, that'll teach - them! */ - } - else - suffix[0] = '\0'; - - /* set paths needed to perform the query - - example format - cache_path: $HOME/.debuginfod_cache - target_cache_dir: $HOME/.debuginfod_cache/0123abcd - target_cache_path: $HOME/.debuginfod_cache/0123abcd/debuginfo - target_cache_path: $HOME/.debuginfod_cache/0123abcd/source#PATH#TO#SOURCE ? - */ - - if (getenv(cache_path_envvar)) - strcpy(cache_path, getenv(cache_path_envvar)); - else - { - if (getenv("HOME")) - sprintf(cache_path, "%s/%s", getenv("HOME"), cache_default_name); - else - sprintf(cache_path, "/%s", cache_default_name); - } - - /* avoid using snprintf here due to compiler warning. */ - snprintf(target_cache_dir, sizeof(target_cache_dir), "%s/%s", cache_path, build_id_bytes); - snprintf(target_cache_path, sizeof(target_cache_path), "%s/%s%s", target_cache_dir, type, suffix); - snprintf(target_cache_tmppath, sizeof(target_cache_tmppath), "%s.XXXXXX", target_cache_path); - - /* XXX combine these */ - snprintf(interval_path, sizeof(interval_path), "%s/%s", cache_path, cache_clean_interval_filename); - snprintf(maxage_path, sizeof(maxage_path), "%s/%s", cache_path, cache_max_unused_age_filename); - rc = debuginfod_init_cache(cache_path, interval_path, maxage_path); - if (rc != 0) - goto out; - rc = debuginfod_clean_cache(c, cache_path, interval_path, maxage_path); - if (rc != 0) - goto out; - - /* If the target is already in the cache then we are done. */ - int fd = open (target_cache_path, O_RDONLY); - if (fd >= 0) - { - /* Success!!!! */ - if (path != NULL) - *path = strdup(target_cache_path); - return fd; - } - - if (getenv(server_timeout_envvar)) - server_timeout = atoi (getenv(server_timeout_envvar)); - - /* make a copy of the envvar so it can be safely modified. */ - server_urls = strdup(urls_envvar); - if (server_urls == NULL) - { - rc = -ENOMEM; - goto out; - } - /* thereafter, goto out0 on error*/ - - /* create target directory in cache if not found. */ - struct stat st; - if (stat(target_cache_dir, &st) == -1 && mkdir(target_cache_dir, 0700) < 0) - { - rc = -errno; - goto out0; - } - - /* NB: write to a temporary file first, to avoid race condition of - multiple clients checking the cache, while a partially-written or empty - file is in there, being written from libcurl. */ - fd = mkstemp (target_cache_tmppath); - if (fd < 0) - { - rc = -errno; - goto out0; - } - - /* Count number of URLs. */ - int num_urls = 0; - for (int i = 0; server_urls[i] != '\0'; i++) - if (server_urls[i] != url_delim_char - && (i == 0 || server_urls[i - 1] == url_delim_char)) - num_urls++; - - /* Tracks which handle should write to fd. Set to the first - handle that is ready to write the target file to the cache. */ - CURL *target_handle = NULL; - struct handle_data *data = malloc(sizeof(struct handle_data) * num_urls); - - /* Initalize handle_data with default values. */ - for (int i = 0; i < num_urls; i++) - { - data[i].handle = NULL; - data[i].fd = -1; - } - - CURLM *curlm = curl_multi_init(); - if (curlm == NULL) - { - rc = -ENETUNREACH; - goto out0; - } - /* thereafter, goto out1 on error. */ - - char *strtok_saveptr; - char *server_url = strtok_r(server_urls, url_delim, &strtok_saveptr); - - /* Initialize each handle. */ - for (int i = 0; i < num_urls && server_url != NULL; i++) - { - data[i].fd = fd; - data[i].target_handle = &target_handle; - data[i].handle = curl_easy_init(); - - if (data[i].handle == NULL) - { - rc = -ENETUNREACH; - goto out1; - } - - /* Build handle url. Tolerate both http://foo:999 and - http://foo:999/ forms */ - char *slashbuildid; - if (strlen(server_url) > 1 && server_url[strlen(server_url)-1] == '/') - slashbuildid = "buildid"; - else - slashbuildid = "/buildid"; - - if (filename) /* must start with / */ - snprintf(data[i].url, PATH_MAX, "%s%s/%s/%s%s", server_url, - slashbuildid, build_id_bytes, type, filename); - else - snprintf(data[i].url, PATH_MAX, "%s%s/%s/%s", server_url, - slashbuildid, build_id_bytes, type); - - curl_easy_setopt(data[i].handle, CURLOPT_URL, data[i].url); - curl_easy_setopt(data[i].handle, - CURLOPT_WRITEFUNCTION, - debuginfod_write_callback); - curl_easy_setopt(data[i].handle, CURLOPT_WRITEDATA, (void*)&data[i]); - curl_easy_setopt(data[i].handle, CURLOPT_TIMEOUT, (long) server_timeout); - curl_easy_setopt(data[i].handle, CURLOPT_FILETIME, (long) 1); - curl_easy_setopt(data[i].handle, CURLOPT_FOLLOWLOCATION, (long) 1); - curl_easy_setopt(data[i].handle, CURLOPT_FAILONERROR, (long) 1); - curl_easy_setopt(data[i].handle, CURLOPT_NOSIGNAL, (long) 1); - curl_easy_setopt(data[i].handle, CURLOPT_AUTOREFERER, (long) 1); - curl_easy_setopt(data[i].handle, CURLOPT_ACCEPT_ENCODING, ""); - curl_easy_setopt(data[i].handle, CURLOPT_USERAGENT, (void*) PACKAGE_STRING); - - curl_multi_add_handle(curlm, data[i].handle); - server_url = strtok_r(NULL, url_delim, &strtok_saveptr); - } - - /* Query servers in parallel. */ - int still_running; - long loops = 0; - do - { - if (c->progressfn) /* inform/check progress callback */ - { - loops ++; - long pa = loops; /* default params for progress callback */ - long pb = 0; - if (target_handle) /* we've committed to a server; report its download progress */ - { - CURLcode curl_res; -#ifdef CURLINFO_SIZE_DOWNLOAD_T - curl_off_t dl; - curl_res = curl_easy_getinfo(target_handle, - CURLINFO_SIZE_DOWNLOAD_T, - &dl); - if (curl_res == 0 && dl >= 0) - pa = (dl > LONG_MAX ? LONG_MAX : (long)dl); -#else - double dl; - curl_res = curl_easy_getinfo(target_handle, - CURLINFO_SIZE_DOWNLOAD, - &dl); - if (curl_res == 0) - pa = (dl > LONG_MAX ? LONG_MAX : (long)dl); -#endif - -#ifdef CURLINFO_CURLINFO_CONTENT_LENGTH_DOWNLOAD_T - curl_off_t cl; - curl_res = curl_easy_getinfo(target_handle, - CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, - &cl); - if (curl_res == 0 && cl >= 0) - pb = (cl > LONG_MAX ? LONG_MAX : (long)cl); -#else - double cl; - curl_res = curl_easy_getinfo(target_handle, - CURLINFO_CONTENT_LENGTH_DOWNLOAD, - &cl); - if (curl_res == 0) - pb = (cl > LONG_MAX ? LONG_MAX : (long)cl); -#endif - } - - if ((*c->progressfn) (c, pa, pb)) - break; - } - - /* Wait 1 second, the minimum DEBUGINFOD_TIMEOUT. */ - curl_multi_wait(curlm, NULL, 0, 1000, NULL); - - /* If the target file has been found, abort the other queries. */ - if (target_handle != NULL) - for (int i = 0; i < num_urls; i++) - if (data[i].handle != target_handle) - curl_multi_remove_handle(curlm, data[i].handle); - - CURLMcode curlm_res = curl_multi_perform(curlm, &still_running); - if (curlm_res != CURLM_OK) - { - switch (curlm_res) - { - case CURLM_CALL_MULTI_PERFORM: continue; - case CURLM_OUT_OF_MEMORY: rc = -ENOMEM; break; - default: rc = -ENETUNREACH; break; - } - goto out1; - } - } while (still_running); - - /* Check whether a query was successful. If so, assign its handle - to verified_handle. */ - int num_msg; - rc = -ENOENT; - CURL *verified_handle = NULL; - do - { - CURLMsg *msg; - - msg = curl_multi_info_read(curlm, &num_msg); - if (msg != NULL && msg->msg == CURLMSG_DONE) - { - if (msg->data.result != CURLE_OK) - { - /* Unsucessful query, determine error code. */ - switch (msg->data.result) - { - case CURLE_COULDNT_RESOLVE_HOST: rc = -EHOSTUNREACH; break; // no NXDOMAIN - case CURLE_URL_MALFORMAT: rc = -EINVAL; break; - case CURLE_COULDNT_CONNECT: rc = -ECONNREFUSED; break; - case CURLE_REMOTE_ACCESS_DENIED: rc = -EACCES; break; - case CURLE_WRITE_ERROR: rc = -EIO; break; - case CURLE_OUT_OF_MEMORY: rc = -ENOMEM; break; - case CURLE_TOO_MANY_REDIRECTS: rc = -EMLINK; break; - case CURLE_SEND_ERROR: rc = -ECONNRESET; break; - case CURLE_RECV_ERROR: rc = -ECONNRESET; break; - case CURLE_OPERATION_TIMEDOUT: rc = -ETIME; break; - default: rc = -ENOENT; break; - } - } - else - { - /* Query completed without an error. Confirm that the - response code is 200 and set verified_handle. */ - long resp_code = 500; - CURLcode curl_res; - - curl_res = curl_easy_getinfo(target_handle, - CURLINFO_RESPONSE_CODE, - &resp_code); - - if (curl_res == CURLE_OK - && resp_code == 200 - && msg->easy_handle != NULL) - { - verified_handle = msg->easy_handle; - break; - } - } - } - } while (num_msg > 0); - - if (verified_handle == NULL) - goto out1; - - /* we've got one!!!! */ - time_t mtime; - CURLcode curl_res = curl_easy_getinfo(verified_handle, CURLINFO_FILETIME, (void*) &mtime); - if (curl_res != CURLE_OK) - mtime = time(NULL); /* fall back to current time */ - - struct timeval tvs[2]; - tvs[0].tv_sec = tvs[1].tv_sec = mtime; - tvs[0].tv_usec = tvs[1].tv_usec = 0; - (void) futimes (fd, tvs); /* best effort */ - - /* rename tmp->real */ - rc = rename (target_cache_tmppath, target_cache_path); - if (rc < 0) - { - rc = -errno; - goto out1; - /* Perhaps we need not give up right away; could retry or something ... */ - } - - /* Success!!!! */ - for (int i = 0; i < num_urls; i++) - curl_easy_cleanup(data[i].handle); - - curl_multi_cleanup (curlm); - free (data); - free (server_urls); - /* don't close fd - we're returning it */ - /* don't unlink the tmppath; it's already been renamed. */ - if (path != NULL) - *path = strdup(target_cache_path); - - return fd; - -/* error exits */ - out1: - for (int i = 0; i < num_urls; i++) - curl_easy_cleanup(data[i].handle); - - curl_multi_cleanup(curlm); - unlink (target_cache_tmppath); - (void) rmdir (target_cache_dir); /* nop if not empty */ - free(data); - close (fd); - - out0: - free (server_urls); - - out: - return rc; -} - -/* See debuginfod.h */ -debuginfod_client * -debuginfod_begin (void) -{ - debuginfod_client *client; - size_t size = sizeof (struct debuginfod_client); - client = (debuginfod_client *) malloc (size); - if (client != NULL) - client->progressfn = NULL; - return client; -} - -void -debuginfod_end (debuginfod_client *client) -{ - free (client); -} - -int -debuginfod_find_debuginfo (debuginfod_client *client, - const unsigned char *build_id, int build_id_len, - char **path) -{ - return debuginfod_query_server(client, build_id, build_id_len, - "debuginfo", NULL, path); -} - - -/* See debuginfod.h */ -int -debuginfod_find_executable(debuginfod_client *client, - const unsigned char *build_id, int build_id_len, - char **path) -{ - return debuginfod_query_server(client, build_id, build_id_len, - "executable", NULL, path); -} - -/* See debuginfod.h */ -int debuginfod_find_source(debuginfod_client *client, - const unsigned char *build_id, int build_id_len, - const char *filename, char **path) -{ - return debuginfod_query_server(client, build_id, build_id_len, - "source", filename, path); -} - - -void -debuginfod_set_progressfn(debuginfod_client *client, - debuginfod_progressfn_t fn) -{ - client->progressfn = fn; -} - - -/* NB: these are thread-unsafe. */ -__attribute__((constructor)) attribute_hidden void libdebuginfod_ctor(void) -{ - curl_global_init(CURL_GLOBAL_DEFAULT); -} - -/* NB: this is very thread-unsafe: it breaks other threads that are still in libcurl */ -__attribute__((destructor)) attribute_hidden void libdebuginfod_dtor(void) -{ - /* ... so don't do this: */ - /* curl_global_cleanup(); */ -} diff --git a/debuginfod/debuginfod.h b/debuginfod/debuginfod.h index 33fae86..6b1b1cc 100644 --- a/debuginfod/debuginfod.h +++ b/debuginfod/debuginfod.h @@ -33,7 +33,6 @@ #define DEBUGINFOD_URLS_ENV_VAR "DEBUGINFOD_URLS" #define DEBUGINFOD_CACHE_PATH_ENV_VAR "DEBUGINFOD_CACHE_PATH" #define DEBUGINFOD_TIMEOUT_ENV_VAR "DEBUGINFOD_TIMEOUT" -#define DEBUGINFOD_PROGRESS_ENV_VAR "DEBUGINFOD_PROGRESS" /* Handle for debuginfod-client connection. */ typedef struct debuginfod_client debuginfod_client; diff --git a/debuginfod/debuginfod.h.debuginfod-timeout-progress b/debuginfod/debuginfod.h.debuginfod-timeout-progress deleted file mode 100644 index 6b1b1cc..0000000 --- a/debuginfod/debuginfod.h.debuginfod-timeout-progress +++ /dev/null @@ -1,85 +0,0 @@ -/* External declarations for the libdebuginfod client library. - Copyright (C) 2019 Red Hat, Inc. - This file is part of elfutils. - - This file is free software; you can redistribute it and/or modify - it under the terms of either - - * the GNU Lesser General Public License as published by the Free - Software Foundation; either version 3 of the License, or (at - your option) any later version - - or - - * the GNU General Public License as published by the Free - Software Foundation; either version 2 of the License, or (at - your option) any later version - - or both in parallel, as here. - - elfutils 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 copies of the GNU General Public License and - the GNU Lesser General Public License along with this program. If - not, see . */ - -#ifndef _DEBUGINFOD_CLIENT_H -#define _DEBUGINFOD_CLIENT_H 1 - -/* Names of environment variables that control the client logic. */ -#define DEBUGINFOD_URLS_ENV_VAR "DEBUGINFOD_URLS" -#define DEBUGINFOD_CACHE_PATH_ENV_VAR "DEBUGINFOD_CACHE_PATH" -#define DEBUGINFOD_TIMEOUT_ENV_VAR "DEBUGINFOD_TIMEOUT" - -/* Handle for debuginfod-client connection. */ -typedef struct debuginfod_client debuginfod_client; - -#ifdef __cplusplus -extern "C" { -#endif - -/* Create a handle for a new debuginfod-client session. */ -debuginfod_client *debuginfod_begin (void); - -/* Query the urls contained in $DEBUGINFOD_URLS for a file with - the specified type and build id. If build_id_len == 0, the - build_id is supplied as a lowercase hexadecimal string; otherwise - it is a binary blob of given legnth. - - If successful, return a file descriptor to the target, otherwise - return a posix error code. If successful, set *path to a - strdup'd copy of the name of the same file in the cache. - Caller must free() it later. */ - -int debuginfod_find_debuginfo (debuginfod_client *client, - const unsigned char *build_id, - int build_id_len, - char **path); - -int debuginfod_find_executable (debuginfod_client *client, - const unsigned char *build_id, - int build_id_len, - char **path); - -int debuginfod_find_source (debuginfod_client *client, - const unsigned char *build_id, - int build_id_len, - const char *filename, - char **path); - -typedef int (*debuginfod_progressfn_t)(debuginfod_client *c, long a, long b); -void debuginfod_set_progressfn(debuginfod_client *c, - debuginfod_progressfn_t fn); - -/* Release debuginfod client connection context handle. */ -void debuginfod_end (debuginfod_client *client); - -#ifdef __cplusplus -} -#endif - - -#endif /* _DEBUGINFOD_CLIENT_H */ diff --git a/doc/debuginfod-find.1 b/doc/debuginfod-find.1 index e71ca29..a759ecb 100644 --- a/doc/debuginfod-find.1 +++ b/doc/debuginfod-find.1 @@ -120,9 +120,8 @@ debuginfod instances. Alternate URL prefixes are separated by space. .TP 21 .B DEBUGINFOD_TIMEOUT This environment variable governs the timeout for each debuginfod HTTP -connection. A server that fails to provide at least 100K of data -within this many seconds is skipped. The default is 90 seconds. (Zero -or negative means "no timeout".) +connection. A server that fails to respond within this many seconds +is skipped. The default is 5. .TP 21 .B DEBUGINFOD_CACHE_PATH diff --git a/doc/debuginfod-find.1.debuginfod-timeout-progress b/doc/debuginfod-find.1.debuginfod-timeout-progress deleted file mode 100644 index a759ecb..0000000 --- a/doc/debuginfod-find.1.debuginfod-timeout-progress +++ /dev/null @@ -1,144 +0,0 @@ -'\"! tbl | nroff \-man -'\" t macro stdmacro - -.de SAMPLE -.br -.RS 0 -.nf -.nh -.. -.de ESAMPLE -.hy -.fi -.RE -.. - -.TH DEBUGINFOD-FIND 1 -.SH NAME -debuginfod-find \- request debuginfo-related data - -.SH SYNOPSIS -.B debuginfod-find [\fIOPTION\fP]... debuginfo \fIBUILDID\fP - -.B debuginfod-find [\fIOPTION\fP]... executable \fIBUILDID\fP - -.B debuginfod-find [\fIOPTION\fP]... source \fIBUILDID\fP \fI/FILENAME\fP - -.SH DESCRIPTION -\fBdebuginfod-find\fP queries one or more \fBdebuginfod\fP servers for -debuginfo-related data. In case of a match, it saves the the -requested file into a local cache, prints the file name to standard -output, and exits with a success status of 0. In case of any error, -it exits with a failure status and an error message to standard error. - -.\" Much of the following text is duplicated with debuginfod.8 - -The debuginfod system uses buildids to identify debuginfo-related data. -These are stored as binary notes in ELF/DWARF files, and are -represented as lowercase hexadecimal. For example, for a program -/bin/ls, look at the ELF note GNU_BUILD_ID: - -.SAMPLE -% readelf -n /bin/ls | grep -A4 build.id -Note section [ 4] '.note.gnu.buildid' of 36 bytes at offset 0x340: -Owner Data size Type -GNU 20 GNU_BUILD_ID -Build ID: 8713b9c3fb8a720137a4a08b325905c7aaf8429d -.ESAMPLE - -Then the hexadecimal BUILDID is simply: - -.SAMPLE -8713b9c3fb8a720137a4a08b325905c7aaf8429d -.ESAMPLE - -.SS debuginfo \fIBUILDID\fP - -If the given buildid is known to a server, this request will result -in a binary object that contains the customary \fB.*debug_*\fP -sections. This may be a split debuginfo file as created by -\fBstrip\fP, or it may be an original unstripped executable. - -.SS executable \fIBUILDID\fP - -If the given buildid is known to the server, this request will result -in a binary object that contains the normal executable segments. This -may be a executable stripped by \fBstrip\fP, or it may be an original -unstripped executable. \fBET_DYN\fP shared libraries are considered -to be a type of executable. - -.SS source \fIBUILDID\fP \fI/SOURCE/FILE\fP - -If the given buildid is known to the server, this request will result -in a binary object that contains the source file mentioned. The path -should be absolute. Relative path names commonly appear in the DWARF -file's source directory, but these paths are relative to -individual compilation unit AT_comp_dir paths, and yet an executable -is made up of multiple CUs. Therefore, to disambiguate, debuginfod -expects source queries to prefix relative path names with the CU -compilation-directory, followed by a mandatory "/". - -Note: the user should not elide \fB../\fP or \fB/./\fP or extraneous -\fB///\fP sorts of path components in the directory names, because if -this is how those names appear in the DWARF files, that is what -debuginfod needs to see too. - -For example: -.TS -l l. -#include source BUILDID /usr/include/stdio.h -/path/to/foo.c source BUILDID /path/to/foo.c -\../bar/foo.c AT_comp_dir=/zoo/ source BUILDID /zoo//../bar/foo.c -.TE - -.SH "OPTIONS" - -.TP -.B "\-v" -Increase verbosity, including printing frequent download-progress messages. - - -.SH "SECURITY" - -debuginfod-find \fBdoes not\fP include any particular security -features. It trusts that the binaries returned by the debuginfod(s) -are accurate. Therefore, the list of servers should include only -trustworthy ones. If accessed across HTTP rather than HTTPS, the -network should be trustworthy. Authentication information through -the internal \fIlibcurl\fP library is not currently enabled, except -for the basic plaintext \%\fIhttp[s]://userid:password@hostname/\fP style. -(The debuginfod server does not perform authentication, but a front-end -proxy server could.) - -.SH "ENVIRONMENT VARIABLES" - -.TP 21 -.B DEBUGINFOD_URLS -This environment variable contains a list of URL prefixes for trusted -debuginfod instances. Alternate URL prefixes are separated by space. - -.TP 21 -.B DEBUGINFOD_TIMEOUT -This environment variable governs the timeout for each debuginfod HTTP -connection. A server that fails to respond within this many seconds -is skipped. The default is 5. - -.TP 21 -.B DEBUGINFOD_CACHE_PATH -This environment variable governs the location of the cache where -downloaded files are kept. It is cleaned periodically as this program -is reexecuted. Cache management parameters may be set by files under -this directory: see the \fBdebuginfod_find_debuginfo(3)\fP man page -for details. The default is $HOME/.debuginfod_client_cache. - -.SH "FILES" -.LP -.PD .1v -.TP 20 -.B $HOME/.debuginfod_client_cache -Default cache directory. -.PD - -.SH "SEE ALSO" -.I "debuginfod(8)" -.I "debuginfod_find_debuginfod(3)" diff --git a/doc/debuginfod.8 b/doc/debuginfod.8 index c55673c..210550e 100644 --- a/doc/debuginfod.8 +++ b/doc/debuginfod.8 @@ -357,10 +357,8 @@ or indirectly - the results would be hilarious. .TP 21 .B DEBUGINFOD_TIMEOUT This environment variable governs the timeout for each debuginfod HTTP -connection. A server that fails to provide at least 100K of data -within this many seconds is skipped. The default is 90 seconds. (Zero -or negative means "no timeout".) - +connection. A server that fails to respond within this many seconds +is skipped. The default is 5. .TP 21 .B DEBUGINFOD_CACHE_PATH diff --git a/doc/debuginfod.8.debuginfod-timeout-progress b/doc/debuginfod.8.debuginfod-timeout-progress deleted file mode 100644 index 210550e..0000000 --- a/doc/debuginfod.8.debuginfod-timeout-progress +++ /dev/null @@ -1,387 +0,0 @@ -'\"! tbl | nroff \-man -'\" t macro stdmacro - -.de SAMPLE -.br -.RS 0 -.nf -.nh -.. -.de ESAMPLE -.hy -.fi -.RE -.. - -.TH DEBUGINFOD 8 -.SH NAME -debuginfod \- debuginfo-related http file-server daemon - -.SH SYNOPSIS -.B debuginfod -[\fIOPTION\fP]... [\fIPATH\fP]... - -.SH DESCRIPTION -\fBdebuginfod\fP serves debuginfo-related artifacts over HTTP. It -periodically scans a set of directories for ELF/DWARF files and their -associated source code, as well as RPM files containing the above, to -build an index by their buildid. This index is used when remote -clients use the HTTP webapi, to fetch these files by the same buildid. - -If a debuginfod cannot service a given buildid artifact request -itself, and it is configured with information about upstream -debuginfod servers, it queries them for the same information, just as -\fBdebuginfod-find\fP would. If successful, it locally caches then -relays the file content to the original requester. - -If the \fB\-F\fP option is given, each listed PATH creates a thread to -scan for matching ELF/DWARF/source files under the given physical -directory. Source files are matched with DWARF files based on the -AT_comp_dir (compilation directory) attributes inside it. Duplicate -directories are ignored. You may use a file name for a PATH, but -source code indexing may be incomplete; prefer using a directory that -contains the binaries. Caution: source files listed in the DWARF may -be a path \fIanywhere\fP in the file system, and debuginfod will -readily serve their content on demand. (Imagine a doctored DWARF file -that lists \fI/etc/passwd\fP as a source file.) If this is a concern, -audit your binaries with tools such as: - -.SAMPLE -% eu-readelf -wline BINARY | sed -n '/^Directory.table/,/^File.name.table/p' -or -% eu-readelf -wline BINARY | sed -n '/^Directory.table/,/^Line.number/p' -or even use debuginfod itself: -% debuginfod -vvv -d :memory: -F BINARY 2>&1 | grep 'recorded.*source' -^C -.ESAMPLE - -If the \fB\-R\fP option is given each listed PATH creates a thread to -scan for ELF/DWARF/source files contained in matching RPMs under the -given physical directory. Duplicate directories are ignored. You may -use a file name for a PATH, but source code indexing may be -incomplete; prefer using a directory that contains normal RPMs -alongside debuginfo/debugsource RPMs. Because of complications such -as DWZ-compressed debuginfo, may require \fItwo\fP scan passes to -identify all source code. Source files for RPMs are only served -from other RPMs, so the caution for \-F does not apply. - -If no PATH is listed, or neither \-F nor \-R option is given, then -\fBdebuginfod\fP will simply serve content that it scanned into its -index in previous runs: the data is cumulative. - -File names must match extended regular expressions given by the \-I -option and not the \-X option (if any) in order to be considered. - - -.SH OPTIONS - -.TP -.B "\-F" -Activate ELF/DWARF file scanning threads. The default is off. - -.TP -.B "\-R" -Activate RPM file scanning threads. The default is off. - -.TP -.B "\-d FILE" "\-\-database=FILE" -Set the path of the sqlite database used to store the index. This -file is disposable in the sense that a later rescan will repopulate -data. It will contain absolute file path names, so it may not be -portable across machines. It may be frequently read/written, so it -should be on a fast filesytem. It should not be shared across -machines or users, to maximize sqlite locking performance. The -default database file is $HOME/.debuginfod.sqlite. - -.TP -.B "\-D SQL" "\-\-ddl=SQL" -Execute given sqlite statement after the database is opened and -initialized as extra DDL (SQL data definition language). This may be -useful to tune performance-related pragmas or indexes. May be -repeated. The default is nothing extra. - -.TP -.B "\-p NUM" "\-\-port=NUM" -Set the TCP port number on which debuginfod should listen, to service -HTTP requests. Both IPv4 and IPV6 sockets are opened, if possible. -The webapi is documented below. The default port number is 8002. - -.TP -.B "\-I REGEX" "\-\-include=REGEX" "\-X REGEX" "\-\-exclude=REGEX" -Govern the inclusion and exclusion of file names under the search -paths. The regular expressions are interpreted as unanchored POSIX -extended REs, thus may include alternation. They are evaluated -against the full path of each file, based on its \fBrealpath(3)\fP -canonicalization. By default, all files are included and none are -excluded. A file that matches both include and exclude REGEX is -excluded. (The \fIcontents\fP of RPM files are not subject to -inclusion or exclusion filtering: they are all processed.) - -.TP -.B "\-t SECONDS" "\-\-rescan\-time=SECONDS" -Set the rescan time for the file and RPM directories. This is the -amount of time the scanning threads will wait after finishing a scan, -before doing it again. A rescan for unchanged files is fast (because -the index also stores the file mtimes). A time of zero is acceptable, -and means that only one initial scan should performed. The default -rescan time is 300 seconds. Receiving a SIGUSR1 signal triggers a new -scan, independent of the rescan time (including if it was zero). - -.TP -.B "\-g SECONDS" "\-\-groom\-time=SECONDS" -Set the groom time for the index database. This is the amount of time -the grooming thread will wait after finishing a grooming pass before -doing it again. A groom operation quickly rescans all previously -scanned files, only to see if they are still present and current, so -it can deindex obsolete files. See also the \fIDATA MANAGEMENT\fP -section. The default groom time is 86400 seconds (1 day). A time of -zero is acceptable, and means that only one initial groom should be -performed. Receiving a SIGUSR2 signal triggers a new grooming pass, -independent of the groom time (including if it was zero). - -.TP -.B "\-G" -Run an extraordinary maximal-grooming pass at debuginfod startup. -This pass can take considerable time, because it tries to remove any -debuginfo-unrelated content from the RPM-related parts of the index. -It should not be run if any recent RPM-related indexing operations -were aborted early. It can take considerable space, because it -finishes up with an sqlite "vacuum" operation, which repacks the -database file by triplicating it temporarily. The default is not to -do maximal-grooming. See also the \fIDATA MANAGEMENT\fP section. - -.TP -.B "\-c NUM" "\-\-concurrency=NUM" -Set the concurrency limit for all the scanning threads. While many -threads may be spawned to cover all the given PATHs, only NUM may -concurrently do CPU-intensive operations like parsing an ELF file -or an RPM. The default is the number of processors on the system; -the minimum is 1. - -.TP -.B "\-L" -Traverse symbolic links encountered during traversal of the PATHs, -including across devices - as in \fIfind\ -L\fP. The default is to -traverse the physical directory structure only, stay on the same -device, and ignore symlinks - as in \fIfind\ -P\ -xdev\fP. Caution: a -loops in the symbolic directory tree might lead to \fIinfinite -traversal\fP. - -.TP -.B "\-v" -Increase verbosity of logging to the standard error file descriptor. -May be repeated to increase details. The default verbosity is 0. - -.SH WEBAPI - -.\" Much of the following text is duplicated with debuginfod-find.1 - -debuginfod's webapi resembles ordinary file service, where a GET -request with a path containing a known buildid results in a file. -Unknown buildid / request combinations result in HTTP error codes. -This file service resemblance is intentional, so that an installation -can take advantage of standard HTTP management infrastructure. - -There are three requests. In each case, the buildid is encoded as a -lowercase hexadecimal string. For example, for a program \fI/bin/ls\fP, -look at the ELF note GNU_BUILD_ID: - -.SAMPLE -% readelf -n /bin/ls | grep -A4 build.id -Note section [ 4] '.note.gnu.buildid' of 36 bytes at offset 0x340: -Owner Data size Type -GNU 20 GNU_BUILD_ID -Build ID: 8713b9c3fb8a720137a4a08b325905c7aaf8429d -.ESAMPLE - -Then the hexadecimal BUILDID is simply: - -.SAMPLE -8713b9c3fb8a720137a4a08b325905c7aaf8429d -.ESAMPLE - -.SS /buildid/\fIBUILDID\fP/debuginfo - -If the given buildid is known to the server, this request will result -in a binary object that contains the customary \fB.*debug_*\fP -sections. This may be a split debuginfo file as created by -\fBstrip\fP, or it may be an original unstripped executable. - -.SS /buildid/\fIBUILDID\fP/executable - -If the given buildid is known to the server, this request will result -in a binary object that contains the normal executable segments. This -may be a executable stripped by \fBstrip\fP, or it may be an original -unstripped executable. \fBET_DYN\fP shared libraries are considered -to be a type of executable. - -.SS /buildid/\fIBUILDID\fP/source\fI/SOURCE/FILE\fP - -If the given buildid is known to the server, this request will result -in a binary object that contains the source file mentioned. The path -should be absolute. Relative path names commonly appear in the DWARF -file's source directory, but these paths are relative to -individual compilation unit AT_comp_dir paths, and yet an executable -is made up of multiple CUs. Therefore, to disambiguate, debuginfod -expects source queries to prefix relative path names with the CU -compilation-directory, followed by a mandatory "/". - -Note: contrary to RFC 3986, the client should not elide \fB../\fP or -\fB/./\fP or extraneous \fB///\fP sorts of path components in the -directory names, because if this is how those names appear in the -DWARF files, that is what debuginfod needs to see too. - -For example: -.TS -l l. -#include /buildid/BUILDID/source/usr/include/stdio.h -/path/to/foo.c /buildid/BUILDID/source/path/to/foo.c -\../bar/foo.c AT_comp_dir=/zoo/ /buildid/BUILDID/source/zoo//../bar/foo.c -.TE - -.SS /metrics - -This endpoint returns a Prometheus formatted text/plain dump of a -variety of statistics about the operation of the debuginfod server. -The exact set of metrics and their meanings may change in future -versions. Caution: configuration information (path names, versions) -may be disclosed. - -.SH DATA MANAGEMENT - -debuginfod stores its index in an sqlite database in a densely packed -set of interlinked tables. While the representation is as efficient -as we have been able to make it, it still takes a considerable amount -of data to record all debuginfo-related data of potentially a great -many files. This section offers some advice about the implications. - -As a general explanation for size, consider that debuginfod indexes -ELF/DWARF files, it stores their names and referenced source file -names, and buildids will be stored. When indexing RPMs, it stores -every file name \fIof or in\fP an RPM, every buildid, plus every -source file name referenced from a DWARF file. (Indexing RPMs takes -more space because the source files often reside in separate -subpackages that may not be indexed at the same pass, so extra -metadata has to be kept.) - -Getting down to numbers, in the case of Fedora RPMs (essentially, -gzip-compressed cpio files), the sqlite index database tends to be -from 0.5% to 3% of their size. It's larger for binaries that are -assembled out of a great many source files, or packages that carry -much debuginfo-unrelated content. It may be even larger during the -indexing phase due to temporary sqlite write-ahead-logging files; -these are checkpointed (cleaned out and removed) at shutdown. It may -be helpful to apply tight \-I or \-X regular-expression constraints to -exclude files from scanning that you know have no debuginfo-relevant -content. - -As debuginfod runs, it periodically rescans its target directories, -and any new content found is added to the database. Old content, such -as data for files that have disappeared or that have been replaced -with newer versions is removed at a periodic \fIgrooming\fP pass. -This means that the sqlite files grow fast during initial indexing, -slowly during index rescans, and periodically shrink during grooming. -There is also an optional one-shot \fImaximal grooming\fP pass is -available. It removes information debuginfo-unrelated data from the -RPM content index such as file names found in RPMs ("rpm sdef" -records) that are not referred to as source files from any binaries -find in RPMs ("rpm sref" records). This can save considerable disk -space. However, it is slow and temporarily requires up to twice the -database size as free space. Worse: it may result in missing -source-code info if the RPM traversals were interrupted, so the not -all source file references were known. Use it rarely to polish a -complete index. - -You should ensure that ample disk space remains available. (The flood -of error messages on -ENOSPC is ugly and nagging. But, like for most -other errors, debuginfod will resume when resources permit.) If -necessary, debuginfod can be stopped, the database file moved or -removed, and debuginfod restarted. - -sqlite offers several performance-related options in the form of -pragmas. Some may be useful to fine-tune the defaults plus the -debuginfod extras. The \-D option may be useful to tell debuginfod to -execute the given bits of SQL after the basic schema creation -commands. For example, the "synchronous", "cache_size", -"auto_vacuum", "threads", "journal_mode" pragmas may be fun to tweak -via \-D, if you're searching for peak performance. The "optimize", -"wal_checkpoint" pragmas may be useful to run periodically, outside -debuginfod. The default settings are performance- rather than -reliability-oriented, so a hardware crash might corrupt the database. -In these cases, it may be necessary to manually delete the sqlite -database and start over. - -As debuginfod changes in the future, we may have no choice but to -change the database schema in an incompatible manner. If this -happens, new versions of debuginfod will issue SQL statements to -\fIdrop\fP all prior schema & data, and start over. So, disk space -will not be wasted for retaining a no-longer-useable dataset. - -In summary, if your system can bear a 0.5%-3% index-to-RPM-dataset -size ratio, and slow growth afterwards, you should not need to -worry about disk space. If a system crash corrupts the database, -or you want to force debuginfod to reset and start over, simply -erase the sqlite file before restarting debuginfod. - - -.SH SECURITY - -debuginfod \fBdoes not\fP include any particular security features. -While it is robust with respect to inputs, some abuse is possible. It -forks a new thread for each incoming HTTP request, which could lead to -a denial-of-service in terms of RAM, CPU, disk I/O, or network I/O. -If this is a problem, users are advised to install debuginfod with a -HTTPS reverse-proxy front-end that enforces site policies for -firewalling, authentication, integrity, authorization, and load -control. The \fI/metrics\fP webapi endpoint is probably not -appropriate for disclosure to the public. - -When relaying queries to upstream debuginfods, debuginfod \fBdoes not\fP -include any particular security features. It trusts that the binaries -returned by the debuginfods are accurate. Therefore, the list of -servers should include only trustworthy ones. If accessed across HTTP -rather than HTTPS, the network should be trustworthy. Authentication -information through the internal \fIlibcurl\fP library is not currently -enabled. - - -.SH "ENVIRONMENT VARIABLES" - -.TP 21 -.B DEBUGINFOD_URLS -This environment variable contains a list of URL prefixes for trusted -debuginfod instances. Alternate URL prefixes are separated by space. -Avoid referential loops that cause a server to contact itself, directly -or indirectly - the results would be hilarious. - -.TP 21 -.B DEBUGINFOD_TIMEOUT -This environment variable governs the timeout for each debuginfod HTTP -connection. A server that fails to respond within this many seconds -is skipped. The default is 5. - -.TP 21 -.B DEBUGINFOD_CACHE_PATH -This environment variable governs the location of the cache where -downloaded files are kept. It is cleaned periodically as this -program is reexecuted. The default is $HOME/.debuginfod_client_cache. -.\" XXX describe cache eviction policy - -.SH FILES -.LP -.PD .1v -.TP 20 -.B $HOME/.debuginfod.sqlite -Default database file. -.PD - -.TP 20 -.B $HOME/.debuginfod_client_cache -Default cache directory for content from upstream debuginfods. -.PD - - -.SH "SEE ALSO" -.I "debuginfod-find(1)" -.I "sqlite3(1)" -.I \%https://prometheus.io/docs/instrumenting/exporters/ diff --git a/doc/debuginfod_find_debuginfo.3 b/doc/debuginfod_find_debuginfo.3 index 7e5060f..be8eed0 100644 --- a/doc/debuginfod_find_debuginfo.3 +++ b/doc/debuginfod_find_debuginfo.3 @@ -164,17 +164,8 @@ debuginfod instances. Alternate URL prefixes are separated by space. .TP 21 .B DEBUGINFOD_TIMEOUT This environment variable governs the timeout for each debuginfod HTTP -connection. A server that fails to provide at least 100K of data -within this many seconds is skipped. The default is 90 seconds. (Zero -or negative means "no timeout".) - -.TP 21 -.B DEBUGINFOD_PROGRESS -This environment variable governs the default progress function. If -set, and if a progressfn is not explicitly set, then the library will -configure a default progressfn. This function will append a simple -progress message periodically to stderr. The default is no progress -function output. +connection. A server that fails to respond within this many seconds +is skipped. The default is 5. .TP 21 .B DEBUGINFOD_CACHE_PATH diff --git a/doc/debuginfod_find_debuginfo.3.debuginfod-timeout-progress b/doc/debuginfod_find_debuginfo.3.debuginfod-timeout-progress deleted file mode 100644 index be8eed0..0000000 --- a/doc/debuginfod_find_debuginfo.3.debuginfod-timeout-progress +++ /dev/null @@ -1,242 +0,0 @@ -'\"! tbl | nroff \-man -'\" t macro stdmacro - -.de SAMPLE -.br -.RS 0 -.nf -.nh -.. -.de ESAMPLE -.hy -.fi -.RE -.. - -.TH DEBUGINFOD_FIND_* 3 -.SH NAME -debuginfod_find_debuginfo \- request debuginfo from debuginfod - -.SH SYNOPSIS -.nf -.B #include -.PP -.BI "debuginfod_client *debuginfod_begin(void);" -.BI "void debuginfod_end(debuginfod_client *" client ");" - -.BI "int debuginfod_find_debuginfo(debuginfod_client *" client "," -.BI " const unsigned char *" build_id "," -.BI " int " build_id_len "," -.BI " char ** " path ");" -.BI "int debuginfod_find_executable(debuginfod_client *" client "," -.BI " const unsigned char *" build_id "," -.BI " int " build_id_len "," -.BI " char ** " path ");" -.BI "int debuginfod_find_source(debuginfod_client *" client "," -.BI " const unsigned char *" build_id "," -.BI " int " build_id_len "," -.BI " const char *" filename "," -.BI " char ** " path ");" - -.BI "typedef int (*debuginfo_progressfn_t)(debuginfod_client *" client "," -.BI " long a, long b);" -.BI "void debuginfod_set_progressfn(debuginfod_client *" client "," -.BI " debuginfo_progressfn_t " progressfn ");" - -Link with \fB-ldebuginfod\fP. - -.SH DESCRIPTION - -.BR debuginfod_begin () -creates a \fBdebuginfod_client\fP connection handle that should be used -with all other calls. -.BR debuginfod_end () -should be called on the \fBclient\fP handle to release all state and -storage when done. - -.BR debuginfod_find_debuginfo (), -.BR debuginfod_find_executable (), -and -.BR debuginfod_find_source () -query the debuginfod server URLs contained in -.BR $DEBUGINFOD_URLS -(see below) for the debuginfo, executable or source file with the -given \fIbuild_id\fP. \fIbuild_id\fP should be a pointer to either -a null-terminated, lowercase hex string or a binary blob. If -\fIbuild_id\fP is given as a hex string, \fIbuild_id_len\fP should -be 0. Otherwise \fIbuild_id_len\fP should be the number of bytes in -the binary blob. - -.BR debuginfod_find_source () -also requries a \fIfilename\fP in order to specify a particular -source file. \fIfilename\fP should be an absolute path that includes -the compilation directory of the CU associated with the source file. -Relative path names commonly appear in the DWARF file's source directory, -but these paths are relative to individual compilation unit AT_comp_dir -paths, and yet an executable is made up of multiple CUs. Therefore, to -disambiguate, debuginfod expects source queries to prefix relative path -names with the CU compilation-directory, followed by a mandatory "/". - -Note: the caller should not elide \fB../\fP or \fB/./\fP or extraneous -\fB///\fP sorts of path components in the directory names, because if -this is how those names appear in the DWARF files, that is what -debuginfod needs to see too. - -If \fIpath\fP is not NULL and the query is successful, \fIpath\fP is set -to the path of the file in the cache. The caller must \fBfree\fP() this value. - -The URLs in \fB$DEBUGINFOD_URLS\fP may be queried in parallel. As soon -as a debuginfod server begins transferring the target file all of the -connections to the other servers are closed. - -A \fBclient\fP handle should be used from only one thread at a time. - -.SH "RETURN VALUE" - -\fBdebuginfod_begin\fP returns the \fBdebuginfod_client\fP handle to -use with all other calls. On error \fBNULL\fP will be returned and -\fBerrno\fP will be set. - -If a find family function is successful, the resulting file is saved -to the client cache and a file descriptor to that file is returned. -The caller needs to \fBclose\fP() this descriptor. Otherwise, a -negative error code is returned. - -.SH "PROGRESS CALLBACK" - -As the \fBdebuginfod_find_*\fP() functions may block for seconds or -longer, a progress callback function is called periodically, if -configured with -.BR debuginfod_set_progressfn (). -This function sets a new progress callback function (or NULL) for the -client handle. - -The given callback function is called from the context of each thread -that is invoking any of the other lookup functions. It is given two -numeric parameters that, if thought of as a numerator \fIa\fP and -denominator \fIb\fP, together represent a completion fraction -\fIa/b\fP. The denominator may be zero initially, until a quantity -such as an exact download size becomes known. - -The progress callback function is also the supported way to -\fIinterrupt\fP the download operation. (The library does \fInot\fP -modify or trigger signals.) The progress callback must return 0 to -continue the work, or any other value to stop work as soon as -possible. Consequently, the \fBdebuginfod_find_*\fP() function will -likely return with an error, but might still succeed. - - -.SH "CACHE" -If the query is successful, the \fBdebuginfod_find_*\fP() functions save -the target file to a local cache. The location of the cache is controlled -by the \fB$DEBUGINFOD_CACHE_PATH\fP environment variable (see below). -Cleaning of the cache is controlled by the \fIcache_clean_interval_s\fP -and \fImax_unused_age_s\fP files, which are found in the -\fB$DEBUGINFOD_CACHE_PATH\fP directory. \fIcache_clean_interval_s\fP controls -how frequently the cache is traversed for cleaning and \fImax_unused_age_s\fP -controls how long a file can go unused (fstat(2) atime) before it's -removed from the cache during cleaning. These files should contain only an -ASCII decimal integer representing the interval or max unused age in seconds. -The default is one day and one week, respectively. Values of zero mean "immediately". - -.SH "SECURITY" -.BR debuginfod_find_debuginfo (), -.BR debuginfod_find_executable (), -and -.BR debuginfod_find_source () -\fBdo not\fP include any particular security -features. They trust that the binaries returned by the debuginfod(s) -are accurate. Therefore, the list of servers should include only -trustworthy ones. If accessed across HTTP rather than HTTPS, the -network should be trustworthy. Passing user authentication information -through the internal \fIlibcurl\fP library is not currently enabled, except -for the basic plaintext \%\fIhttp[s]://userid:password@hostname/\fP style. -(The debuginfod server does not perform authentication, but a front-end -proxy server could.) - -.SH "ENVIRONMENT VARIABLES" - -.TP 21 -.B DEBUGINFOD_URLS -This environment variable contains a list of URL prefixes for trusted -debuginfod instances. Alternate URL prefixes are separated by space. - -.TP 21 -.B DEBUGINFOD_TIMEOUT -This environment variable governs the timeout for each debuginfod HTTP -connection. A server that fails to respond within this many seconds -is skipped. The default is 5. - -.TP 21 -.B DEBUGINFOD_CACHE_PATH -This environment variable governs the location of the cache where -downloaded files are kept. It is cleaned periodically as this -program is reexecuted. The default is $HOME/.debuginfod_client_cache. - -.SH "ERRORS" -The following list is not comprehensive. Error codes may also -originate from calls to various C Library functions. - -.TP -.BR EACCESS -Denied access to resource located at the URL. - -.TP -.BR ECONNREFUSED -Unable to connect to remote host. - -.TP -.BR ECONNRESET -Unable to either send or recieve network data. - -.TP -.BR EHOSTUNREACH -Unable to resolve remote host. - -.TP -.BR EINVAL -One or more arguments are incorrectly formatted. \fIbuild_id\fP may -be too long (greater than 256 characters), \fIfilename\fP may not -be an absolute path or a debuginfod URL is malformed. - -.TP -.BR EIO -Unable to write data received from server to local file. - -.TP -.BR EMLINK -Too many HTTP redirects. - -.TP -.BR ENETUNREACH -Unable to initialize network connection. - -.TP -.BR ENOENT -Could not find the resource located at URL. Often this error code -indicates that a debuginfod server was successfully contacted but -the server could not find the target file. - -.TP -.BR ENOMEM -System is unable to allocate resources. - -.TP -.BR ENOSYS -\fB$DEBUGINFOD_URLS\fP is not defined. - -.TP -.BR ETIME -Query failed due to timeout. \fB$DEBUGINFOD_TIMEOUT\fP controls -the timeout duration. See debuginfod(8) for more information. - -.SH "FILES" -.LP -.PD .1v -.TP 20 -.B $HOME/.debuginfod_client_cache -Default cache directory. -.PD - -.SH "SEE ALSO" -.I "debuginfod(8)" diff --git a/libasm/libasm.h b/libasm/libasm.h index a45c9fa..5c61224 100644 --- a/libasm/libasm.h +++ b/libasm/libasm.h @@ -32,7 +32,7 @@ #include #include -typedef struct ebl Ebl; +#include /* Opaque type for the assembler context descriptor. */ diff --git a/libasm/libasm.h.libasm-ebl b/libasm/libasm.h.libasm-ebl deleted file mode 100644 index 5c61224..0000000 --- a/libasm/libasm.h.libasm-ebl +++ /dev/null @@ -1,202 +0,0 @@ -/* Interface for libasm. - Copyright (C) 2002, 2005, 2008 Red Hat, Inc. - This file is part of elfutils. - - This file is free software; you can redistribute it and/or modify - it under the terms of either - - * the GNU Lesser General Public License as published by the Free - Software Foundation; either version 3 of the License, or (at - your option) any later version - - or - - * the GNU General Public License as published by the Free - Software Foundation; either version 2 of the License, or (at - your option) any later version - - or both in parallel, as here. - - elfutils 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 copies of the GNU General Public License and - the GNU Lesser General Public License along with this program. If - not, see . */ - -#ifndef _LIBASM_H -#define _LIBASM_H 1 - -#include -#include - -#include - - -/* Opaque type for the assembler context descriptor. */ -typedef struct AsmCtx AsmCtx_t; - -/* Opaque type for a section. */ -typedef struct AsmScn AsmScn_t; - -/* Opaque type for a section group. */ -typedef struct AsmScnGrp AsmScnGrp_t; - -/* Opaque type for a symbol. */ -typedef struct AsmSym AsmSym_t; - - -/* Opaque type for the disassembler context descriptor. */ -typedef struct DisasmCtx DisasmCtx_t; - -/* Type used for callback functions to retrieve symbol name. The - symbol reference is in the section designated by the second parameter - at an offset described by the first parameter. The value is the - third parameter. */ -typedef int (*DisasmGetSymCB_t) (GElf_Addr, Elf32_Word, GElf_Addr, char **, - size_t *, void *); - -/* Output function callback. */ -typedef int (*DisasmOutputCB_t) (char *, size_t, void *); - - -#ifdef __cplusplus -extern "C" { -#endif - -/* Create output file and return descriptor for assembler context. If - TEXTP is true the output is an assembler format text file. - Otherwise an object file is created. The MACHINE parameter - corresponds to an EM_ constant from , KLASS specifies the - class (32- or 64-bit), and DATA specifies the byte order (little or - big endian). */ -extern AsmCtx_t *asm_begin (const char *fname, Ebl *ebl, bool textp); - -/* Abort the operation on the assembler context and free all resources. */ -extern int asm_abort (AsmCtx_t *ctx); - -/* Finalize output file and free all resources. */ -extern int asm_end (AsmCtx_t *ctx); - - -/* Return handle for the named section. If it was not used before - create it. */ -extern AsmScn_t *asm_newscn (AsmCtx_t *ctx, const char *scnname, - GElf_Word type, GElf_Xword flags); - - -/* Similar to 'asm_newscn', but make it part of section group GRP. */ -extern AsmScn_t *asm_newscn_ingrp (AsmCtx_t *ctx, const char *scnname, - GElf_Word type, GElf_Xword flags, - AsmScnGrp_t *grp); - -/* Create new subsection NR in the given section. */ -extern AsmScn_t *asm_newsubscn (AsmScn_t *asmscn, unsigned int nr); - - -/* Return handle for new section group. The signature symbol can be - set later. */ -extern AsmScnGrp_t *asm_newscngrp (AsmCtx_t *ctx, const char *grpname, - AsmSym_t *signature, Elf32_Word flags); - -/* Set or overwrite signature symbol for group. */ -extern int asm_scngrp_newsignature (AsmScnGrp_t *grp, AsmSym_t *signature); - - -/* Add zero terminated string STR of size LEN to (sub)section ASMSCN. */ -extern int asm_addstrz (AsmScn_t *asmscn, const char *str, size_t len); - -/* Add 8-bit signed integer NUM to (sub)section ASMSCN. */ -extern int asm_addint8 (AsmScn_t *asmscn, int8_t num); - -/* Add 8-bit unsigned integer NUM to (sub)section ASMSCN. */ -extern int asm_adduint8 (AsmScn_t *asmscn, uint8_t num); - -/* Add 16-bit signed integer NUM to (sub)section ASMSCN. */ -extern int asm_addint16 (AsmScn_t *asmscn, int16_t num); - -/* Add 16-bit unsigned integer NUM to (sub)section ASMSCN. */ -extern int asm_adduint16 (AsmScn_t *asmscn, uint16_t num); - -/* Add 32-bit signed integer NUM to (sub)section ASMSCN. */ -extern int asm_addint32 (AsmScn_t *asmscn, int32_t num); - -/* Add 32-bit unsigned integer NUM to (sub)section ASMSCN. */ -extern int asm_adduint32 (AsmScn_t *asmscn, uint32_t num); - -/* Add 64-bit signed integer NUM to (sub)section ASMSCN. */ -extern int asm_addint64 (AsmScn_t *asmscn, int64_t num); - -/* Add 64-bit unsigned integer NUM to (sub)section ASMSCN. */ -extern int asm_adduint64 (AsmScn_t *asmscn, uint64_t num); - - -/* Add signed little endian base 128 integer NUM to (sub)section ASMSCN. */ -extern int asm_addsleb128 (AsmScn_t *asmscn, int32_t num); - -/* Add unsigned little endian base 128 integer NUM to (sub)section ASMSCN. */ -extern int asm_adduleb128 (AsmScn_t *asmscn, uint32_t num); - - -/* Define new symbol NAME for current position in given section ASMSCN. */ -extern AsmSym_t *asm_newsym (AsmScn_t *asmscn, const char *name, - GElf_Xword size, int type, int binding); - - -/* Define new common symbol NAME with given SIZE and alignment. */ -extern AsmSym_t *asm_newcomsym (AsmCtx_t *ctx, const char *name, - GElf_Xword size, GElf_Addr align); - -/* Define new common symbol NAME with given SIZE, VALUE, TYPE, and BINDING. */ -extern AsmSym_t *asm_newabssym (AsmCtx_t *ctx, const char *name, - GElf_Xword size, GElf_Addr value, - int type, int binding); - - -/* Align (sub)section offset according to VALUE. */ -extern int asm_align (AsmScn_t *asmscn, GElf_Word value); - -/* Set the byte pattern used to fill gaps created by alignment. */ -extern int asm_fill (AsmScn_t *asmscn, void *bytes, size_t len); - - -/* Return ELF descriptor created for the output file of the given context. */ -extern Elf *asm_getelf (AsmCtx_t *ctx); - - -/* Return error code of last failing function call. This value is kept - separately for each thread. */ -extern int asm_errno (void); - -/* Return error string for ERROR. If ERROR is zero, return error string - for most recent error or NULL is none occurred. If ERROR is -1 the - behaviour is similar to the last case except that not NULL but a legal - string is returned. */ -extern const char *asm_errmsg (int __error); - - -/* Create context descriptor for disassembler. */ -extern DisasmCtx_t *disasm_begin (Ebl *ebl, Elf *elf, DisasmGetSymCB_t symcb); - -/* Release descriptor for disassembler. */ -extern int disasm_end (DisasmCtx_t *ctx); - -/* Produce of disassembly output for given memory, store text in - provided buffer. */ -extern int disasm_str (DisasmCtx_t *ctx, const uint8_t **startp, - const uint8_t *end, GElf_Addr addr, const char *fmt, - char **bufp, size_t len, void *symcbarg); - -/* Produce disassembly output for given memory and output it using the - given callback functions. */ -extern int disasm_cb (DisasmCtx_t *ctx, const uint8_t **startp, - const uint8_t *end, GElf_Addr addr, const char *fmt, - DisasmOutputCB_t outcb, void *outcbarg, void *symcbarg); - -#ifdef __cplusplus -} -#endif - -#endif /* libasm.h */ diff --git a/libasm/libasmP.h b/libasm/libasmP.h index a4703fc..54460cf 100644 --- a/libasm/libasmP.h +++ b/libasm/libasmP.h @@ -31,7 +31,6 @@ #include -#include "libebl.h" #include #include "libdwelf.h" diff --git a/libasm/libasmP.h.libasm-ebl b/libasm/libasmP.h.libasm-ebl deleted file mode 100644 index 54460cf..0000000 --- a/libasm/libasmP.h.libasm-ebl +++ /dev/null @@ -1,309 +0,0 @@ -/* Internal definitions for libasm. - Copyright (C) 2002, 2004, 2005, 2016 Red Hat, Inc. - This file is part of elfutils. - - This file is free software; you can redistribute it and/or modify - it under the terms of either - - * the GNU Lesser General Public License as published by the Free - Software Foundation; either version 3 of the License, or (at - your option) any later version - - or - - * the GNU General Public License as published by the Free - Software Foundation; either version 2 of the License, or (at - your option) any later version - - or both in parallel, as here. - - elfutils 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 copies of the GNU General Public License and - the GNU Lesser General Public License along with this program. If - not, see . */ - -#ifndef _LIBASMP_H -#define _LIBASMP_H 1 - -#include - -#include - -#include "libdwelf.h" - -/* gettext helper macros. */ -#define _(Str) dgettext ("elfutils", Str) - - -/* Known error codes. */ -enum - { - ASM_E_NOERROR, - ASM_E_NOMEM, /* No more memory. */ - ASM_E_CANNOT_CREATE, /* Output file cannot be created. */ - ASM_E_INVALID, /* Invalid parameters. */ - ASM_E_CANNOT_CHMOD, /* Cannot change mode of output file. */ - ASM_E_CANNOT_RENAME, /* Cannot rename output file. */ - ASM_E_DUPLSYM, /* Duplicate symbol definition. */ - ASM_E_LIBELF, /* Refer to error in libelf. */ - ASM_E_TYPE, /* Invalid section type for operation. */ - ASM_E_IOERROR, /* Error during output of data. */ - ASM_E_ENOSUP, /* No backend support. */ - ASM_E_NUM /* Keep this entry as the last. */ - }; - - -/* Special sections. */ -#define ASM_ABS_SCN ((Elf_Scn *) 1) -#define ASM_COM_SCN ((Elf_Scn *) 2) - - -/* And the hash table for symbols. */ -#include - - -/* Descriptor for a section. */ -struct AsmScn -{ - /* The underlying assembler context. */ - AsmCtx_t *ctx; - - /* Subsection ID. */ - unsigned int subsection_id; - - /* Section type. */ - GElf_Word type; - - union - { - /* Data only stored in the record for subsection zero. */ - struct - { - /* The ELF section. */ - Elf_Scn *scn; - - /* Entry in the section header string table. */ - Dwelf_Strent *strent; - - /* Next member of group. */ - struct AsmScn *next_in_group; - } main; - - /* Pointer to the record for subsection zero. */ - AsmScn_t *up; - } data; - - /* Current offset in the (sub)section. */ - GElf_Off offset; - /* Maximum alignment of the section so far. */ - GElf_Word max_align; - - /* Section content. */ - struct AsmData - { - /* Currently used number of bytes in the block. */ - size_t len; - - /* Number of bytes allocated. */ - size_t maxlen; - - /* Pointer to the next block. */ - struct AsmData *next; - - /* The actual data. */ - char data[flexarr_size]; - } *content; - - /* Fill pattern. */ - struct FillPattern - { - size_t len; - char bytes[flexarr_size]; - } *pattern; - - /* Next subsection. */ - AsmScn_t *subnext; - - /* List of all allocated sections. */ - AsmScn_t *allnext; - - /* Name of the section. */ - char name[flexarr_size]; -}; - - -/* Descriptor used for the assembling session. */ -struct AsmCtx -{ - /* File descriptor of the temporary file. */ - int fd; - - /* True if text output is wanted. */ - bool textp; - - /* Output file handle. */ - union - { - /* ELF descriptor of the temporary file. */ - Elf *elf; - /* I/O stream for text output. */ - FILE *file; - } out; - - - /* List with defined sections. */ - AsmScn_t *section_list; - /* Section header string table. */ - Dwelf_Strtab *section_strtab; - - /* Table with defined symbols. */ - asm_symbol_tab symbol_tab; - /* Number of symbols in the table. */ - unsigned int nsymbol_tab; - /* Symbol string table. */ - Dwelf_Strtab *symbol_strtab; - - /* List of section groups. */ - struct AsmScnGrp *groups; - /* Number of section groups. */ - size_t ngroups; - - /* Current required alignment for common symbols. */ - GElf_Word common_align; - - /* Lock to handle multithreaded programs. */ - rwlock_define (,lock); - - /* Counter for temporary symbols. */ - unsigned int tempsym_count; - - /* Name of the output file. */ - char *fname; - /* The name of the temporary file. */ - char tmp_fname[flexarr_size]; -}; - - -/* Descriptor for a symbol. */ -struct AsmSym -{ - /* Reference to the section which contains the symbol. */ - AsmScn_t *scn; - - /* Type of the symbol. */ - int8_t type; - /* Binding of the symbol. */ - int8_t binding; - - /* Size of the symbol. */ - GElf_Xword size; - - /* Offset in the section. */ - GElf_Off offset; - - /* Symbol table index of the symbol in the symbol table. */ - size_t symidx; - - /* Reference to name of the symbol. */ - Dwelf_Strent *strent; -}; - - -/* Descriptor for section group. */ -struct AsmScnGrp -{ - /* Entry in the section header string table. */ - Dwelf_Strent *strent; - - /* The ELF section. */ - Elf_Scn *scn; - - /* The signature. */ - struct AsmSym *signature; - - /* First member. */ - struct AsmScn *members; - /* Number of members. */ - size_t nmembers; - - /* Flags. */ - Elf32_Word flags; - - /* Next group. */ - struct AsmScnGrp *next; - - /* Name of the section group. */ - char name[flexarr_size]; -}; - - -/* Descriptor for disassembler. */ -struct DisasmCtx -{ - /* Handle for the backend library with the disassembler routine. */ - Ebl *ebl; - - /* ELF file containing all the data passed to the function. This - allows to look up symbols. */ - Elf *elf; - - /* Callback function to determine symbol names. */ - DisasmGetSymCB_t symcb; -}; - - -/* The default fill pattern: one zero byte. */ -extern const struct FillPattern *__libasm_default_pattern - attribute_hidden; - - -/* Ensure there are at least LEN bytes available in the output buffer - for ASMSCN. */ -extern int __libasm_ensure_section_space (AsmScn_t *asmscn, size_t len) - internal_function; - -/* Free all resources associated with the assembler context. */ -extern void __libasm_finictx (AsmCtx_t *ctx) internal_function; - -/* Set error code. */ -extern void __libasm_seterrno (int err) internal_function; - -/* Return handle for the named section. If it was not used before - create it. */ -extern AsmScn_t *__asm_newscn_internal (AsmCtx_t *ctx, const char *scnname, - GElf_Word type, GElf_Xword flags) - attribute_hidden; - - -/* Internal aliases of the asm_addintXX functions. */ -extern int __asm_addint8_internal (AsmScn_t *asmscn, int8_t num) - attribute_hidden; -extern int __asm_addint16_internal (AsmScn_t *asmscn, int16_t num) - attribute_hidden; -extern int __asm_addint32_internal (AsmScn_t *asmscn, int32_t num) - attribute_hidden; -extern int __asm_addint64_internal (AsmScn_t *asmscn, int64_t num) - attribute_hidden; - - -/* Produce disassembly output for given memory and output it using the - given callback functions. */ -extern int __disasm_cb_internal (DisasmCtx_t *ctx, const uint8_t **startp, - const uint8_t *end, GElf_Addr addr, - const char *fmt, DisasmOutputCB_t outcb, - void *outcbarp, void *symcbarg) - attribute_hidden; - - -/* Test whether given symbol is an internal symbol and if yes, whether - we should nevertheless emit it in the symbol table. */ -// XXX The second part should probably be controlled by an option which -// isn't implemented yet -// XXX Also, the format will change with the backend. -#define asm_emit_symbol_p(name) (strncmp (name, ".L", 2) != 0) - -#endif /* libasmP.h */ diff --git a/libdwfl/linux-kernel-modules.c b/libdwfl/linux-kernel-modules.c index 48fb1ff..d46ab5a 100644 --- a/libdwfl/linux-kernel-modules.c +++ b/libdwfl/linux-kernel-modules.c @@ -174,8 +174,6 @@ kernel_release (void) static int find_kernel_elf (Dwfl *dwfl, const char *release, char **fname) { - /* First try to find an uncompressed vmlinux image. Possibly - including debuginfo. */ if ((release[0] == '/' ? asprintf (fname, "%s/vmlinux", release) : asprintf (fname, "/boot/vmlinux-%s", release)) < 0) @@ -190,27 +188,6 @@ find_kernel_elf (Dwfl *dwfl, const char *release, char **fname) fd = try_kernel_name (dwfl, fname, true); } - /* There might be a compressed vmlinuz image. Probably without - debuginfo, but try to find it under the debug path also, just in - case. */ - if (fd < 0) - { - free (*fname); - if ((release[0] == '/' - ? asprintf (fname, "%s/vmlinuz", release) - : asprintf (fname, "/boot/vmlinuz-%s", release)) < 0) - return -1; - - fd = try_kernel_name (dwfl, fname, true); - if (fd < 0 && release[0] != '/') - { - free (*fname); - if (asprintf (fname, MODULEDIRFMT "/vmlinuz", release) < 0) - return -1; - fd = try_kernel_name (dwfl, fname, true); - } - } - return fd; } diff --git a/libdwfl/linux-kernel-modules.c.vmlinuz b/libdwfl/linux-kernel-modules.c.vmlinuz deleted file mode 100644 index d46ab5a..0000000 --- a/libdwfl/linux-kernel-modules.c.vmlinuz +++ /dev/null @@ -1,1003 +0,0 @@ -/* Standard libdwfl callbacks for debugging the running Linux kernel. - Copyright (C) 2005-2011, 2013, 2014, 2015 Red Hat, Inc. - This file is part of elfutils. - - This file is free software; you can redistribute it and/or modify - it under the terms of either - - * the GNU Lesser General Public License as published by the Free - Software Foundation; either version 3 of the License, or (at - your option) any later version - - or - - * the GNU General Public License as published by the Free - Software Foundation; either version 2 of the License, or (at - your option) any later version - - or both in parallel, as here. - - elfutils 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 copies of the GNU General Public License and - the GNU Lesser General Public License along with this program. If - not, see . */ - -/* In case we have a bad fts we include this before config.h because it - can't handle _FILE_OFFSET_BITS. - Everything we need here is fine if its declarations just come first. - Also, include sys/types.h before fts. On some systems fts.h is not self - contained. */ -#ifdef BAD_FTS - #include - #include -#endif - -#include -#include - -#include "libelfP.h" -#include "libdwflP.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* If fts.h is included before config.h, its indirect inclusions may not - give us the right LFS aliases of these functions, so map them manually. */ -#ifdef BAD_FTS - #ifdef _FILE_OFFSET_BITS - #define open open64 - #define fopen fopen64 - #endif -#else - #include - #include -#endif - - -#define KERNEL_MODNAME "kernel" - -#define MODULEDIRFMT "/lib/modules/%s" - -#define KNOTESFILE "/sys/kernel/notes" -#define MODNOTESFMT "/sys/module/%s/notes" -#define KSYMSFILE "/proc/kallsyms" -#define MODULELIST "/proc/modules" -#define SECADDRDIRFMT "/sys/module/%s/sections/" -#define MODULE_SECT_NAME_LEN 32 /* Minimum any linux/module.h has had. */ - - -static const char *vmlinux_suffixes[] = - { - ".gz", -#ifdef USE_BZLIB - ".bz2", -#endif -#ifdef USE_LZMA - ".xz", -#endif - }; - -/* Try to open the given file as it is or under the debuginfo directory. */ -static int -try_kernel_name (Dwfl *dwfl, char **fname, bool try_debug) -{ - if (*fname == NULL) - return -1; - - /* Don't bother trying *FNAME itself here if the path will cause it to be - tried because we give its own basename as DEBUGLINK_FILE. */ - int fd = ((((dwfl->callbacks->debuginfo_path - ? *dwfl->callbacks->debuginfo_path : NULL) - ?: DEFAULT_DEBUGINFO_PATH)[0] == ':') ? -1 - : TEMP_FAILURE_RETRY (open (*fname, O_RDONLY))); - - if (fd < 0) - { - Dwfl_Module fakemod = { .dwfl = dwfl }; - - if (try_debug) - /* Passing NULL for DEBUGLINK_FILE searches for both the basenamer - "vmlinux" and the default of basename + ".debug", to look for - "vmlinux.debug" files. */ - fd = INTUSE(dwfl_standard_find_debuginfo) (&fakemod, NULL, NULL, 0, - *fname, NULL, 0, - &fakemod.debug.name); - else - /* Try the file's unadorned basename as DEBUGLINK_FILE, - to look only for "vmlinux" files. */ - fd = INTUSE(dwfl_standard_find_debuginfo) (&fakemod, NULL, NULL, 0, - *fname, basename (*fname), - 0, &fakemod.debug.name); - - if (fakemod.debug.name != NULL) - { - free (*fname); - *fname = fakemod.debug.name; - } - } - - if (fd < 0) - for (size_t i = 0; - i < sizeof vmlinux_suffixes / sizeof vmlinux_suffixes[0]; - ++i) - { - char *zname; - if (asprintf (&zname, "%s%s", *fname, vmlinux_suffixes[i]) > 0) - { - fd = TEMP_FAILURE_RETRY (open (zname, O_RDONLY)); - if (fd < 0) - free (zname); - else - { - free (*fname); - *fname = zname; - } - } - } - - if (fd < 0) - { - free (*fname); - *fname = NULL; - } - - return fd; -} - -static inline const char * -kernel_release (void) -{ -#ifdef __linux__ - /* Cache the `uname -r` string we'll use. */ - static struct utsname utsname; - if (utsname.release[0] == '\0' && uname (&utsname) != 0) - return NULL; - return utsname.release; -#else - /* Used for finding the running linux kernel, which isn't supported - on non-linux kernel systems. */ - errno = ENOTSUP; - return NULL; -#endif -} - -static int -find_kernel_elf (Dwfl *dwfl, const char *release, char **fname) -{ - if ((release[0] == '/' - ? asprintf (fname, "%s/vmlinux", release) - : asprintf (fname, "/boot/vmlinux-%s", release)) < 0) - return -1; - - int fd = try_kernel_name (dwfl, fname, true); - if (fd < 0 && release[0] != '/') - { - free (*fname); - if (asprintf (fname, MODULEDIRFMT "/vmlinux", release) < 0) - return -1; - fd = try_kernel_name (dwfl, fname, true); - } - - return fd; -} - -static int -get_release (Dwfl *dwfl, const char **release) -{ - if (dwfl == NULL) - return -1; - - const char *release_string = release == NULL ? NULL : *release; - if (release_string == NULL) - { - release_string = kernel_release (); - if (release_string == NULL) - return errno; - if (release != NULL) - *release = release_string; - } - - return 0; -} - -static int -report_kernel (Dwfl *dwfl, const char **release, - int (*predicate) (const char *module, const char *file)) -{ - int result = get_release (dwfl, release); - if (unlikely (result != 0)) - return result; - - char *fname; - int fd = find_kernel_elf (dwfl, *release, &fname); - - if (fd < 0) - result = ((predicate != NULL && !(*predicate) (KERNEL_MODNAME, NULL)) - ? 0 : errno ?: ENOENT); - else - { - bool report = true; - - if (predicate != NULL) - { - /* Let the predicate decide whether to use this one. */ - int want = (*predicate) (KERNEL_MODNAME, fname); - if (want < 0) - result = errno; - report = want > 0; - } - - if (report) - { - /* Note that on some architectures (e.g. x86_64) the vmlinux - is ET_EXEC, while on others (e.g. ppc64) it is ET_DYN. - In both cases the phdr p_vaddr load address will be non-zero. - We want the image to be placed as if it was ET_DYN, so - pass true for add_p_vaddr which will do the right thing - (in combination with a zero base) in either case. */ - Dwfl_Module *mod = INTUSE(dwfl_report_elf) (dwfl, KERNEL_MODNAME, - fname, fd, 0, true); - if (mod == NULL) - result = -1; - else - /* The kernel is ET_EXEC, but always treat it as relocatable. */ - mod->e_type = ET_DYN; - } - - free (fname); - - if (!report || result < 0) - close (fd); - } - - return result; -} - -/* Look for a kernel debug archive. If we find one, report all its modules. - If not, return ENOENT. */ -static int -report_kernel_archive (Dwfl *dwfl, const char **release, - int (*predicate) (const char *module, const char *file)) -{ - int result = get_release (dwfl, release); - if (unlikely (result != 0)) - return result; - - char *archive; - int res = (((*release)[0] == '/') - ? asprintf (&archive, "%s/debug.a", *release) - : asprintf (&archive, MODULEDIRFMT "/debug.a", *release)); - if (unlikely (res < 0)) - return ENOMEM; - - int fd = try_kernel_name (dwfl, &archive, false); - if (fd < 0) - result = errno ?: ENOENT; - else - { - /* We have the archive file open! */ - Dwfl_Module *last = __libdwfl_report_offline (dwfl, NULL, archive, fd, - true, predicate); - if (unlikely (last == NULL)) - result = -1; - else - { - /* Find the kernel and move it to the head of the list. */ - Dwfl_Module **tailp = &dwfl->modulelist, **prevp = tailp; - for (Dwfl_Module *m = *prevp; m != NULL; m = *(prevp = &m->next)) - if (!m->gc && m->e_type != ET_REL && !strcmp (m->name, "kernel")) - { - *prevp = m->next; - m->next = *tailp; - *tailp = m; - break; - } - } - } - - free (archive); - return result; -} - -static size_t -check_suffix (const FTSENT *f, size_t namelen) -{ -#define TRY(sfx) \ - if ((namelen ? f->fts_namelen == namelen + sizeof sfx - 1 \ - : f->fts_namelen >= sizeof sfx) \ - && !memcmp (f->fts_name + f->fts_namelen - (sizeof sfx - 1), \ - sfx, sizeof sfx)) \ - return sizeof sfx - 1 - - TRY (".ko"); - TRY (".ko.gz"); -#if USE_BZLIB - TRY (".ko.bz2"); -#endif -#if USE_LZMA - TRY (".ko.xz"); -#endif - - return 0; - -#undef TRY -} - -/* Report a kernel and all its modules found on disk, for offline use. - If RELEASE starts with '/', it names a directory to look in; - if not, it names a directory to find under /lib/modules/; - if null, /lib/modules/`uname -r` is used. - Returns zero on success, -1 if dwfl_report_module failed, - or an errno code if finding the files on disk failed. */ -int -dwfl_linux_kernel_report_offline (Dwfl *dwfl, const char *release, - int (*predicate) (const char *module, - const char *file)) -{ - int result = report_kernel_archive (dwfl, &release, predicate); - if (result != ENOENT) - return result; - - /* First report the kernel. */ - result = report_kernel (dwfl, &release, predicate); - if (result == 0) - { - /* Do "find /lib/modules/RELEASE -name *.ko". */ - - char *modulesdir[] = { NULL, NULL }; - if (release[0] == '/') - modulesdir[0] = (char *) release; - else - { - if (asprintf (&modulesdir[0], MODULEDIRFMT, release) < 0) - return errno; - } - - FTS *fts = fts_open (modulesdir, FTS_NOSTAT | FTS_LOGICAL, NULL); - if (modulesdir[0] == (char *) release) - modulesdir[0] = NULL; - if (fts == NULL) - { - free (modulesdir[0]); - return errno; - } - - FTSENT *f; - while ((f = fts_read (fts)) != NULL) - { - /* Skip a "source" subtree, which tends to be large. - This insane hard-coding of names is what depmod does too. */ - if (f->fts_namelen == sizeof "source" - 1 - && !strcmp (f->fts_name, "source")) - { - fts_set (fts, f, FTS_SKIP); - continue; - } - - switch (f->fts_info) - { - case FTS_F: - case FTS_SL: - case FTS_NSOK:; - /* See if this file name matches "*.ko". */ - const size_t suffix = check_suffix (f, 0); - if (suffix) - { - /* We have a .ko file to report. Following the algorithm - by which the kernel makefiles set KBUILD_MODNAME, we - replace all ',' or '-' with '_' in the file name and - call that the module name. Modules could well be - built using different embedded names than their file - names. To handle that, we would have to look at the - __this_module.name contents in the module's text. */ - - char *name = strndup (f->fts_name, f->fts_namelen - suffix); - if (unlikely (name == NULL)) - { - __libdwfl_seterrno (DWFL_E_NOMEM); - result = -1; - break; - } - for (size_t i = 0; i < f->fts_namelen - suffix; ++i) - if (name[i] == '-' || name[i] == ',') - name[i] = '_'; - - if (predicate != NULL) - { - /* Let the predicate decide whether to use this one. */ - int want = (*predicate) (name, f->fts_path); - if (want < 0) - { - result = -1; - free (name); - break; - } - if (!want) - { - free (name); - continue; - } - } - - if (dwfl_report_offline (dwfl, name, f->fts_path, -1) == NULL) - { - free (name); - result = -1; - break; - } - free (name); - } - continue; - - case FTS_ERR: - case FTS_DNR: - case FTS_NS: - result = f->fts_errno; - break; - - case FTS_SLNONE: - default: - continue; - } - - /* We only get here in error cases. */ - break; - } - fts_close (fts); - free (modulesdir[0]); - } - - return result; -} -INTDEF (dwfl_linux_kernel_report_offline) - - -/* State of read_address used by intuit_kernel_bounds. */ -struct read_address_state { - FILE *f; - char *line; - size_t linesz; - size_t n; - char *p; - const char *type; -}; - -static inline bool -read_address (struct read_address_state *state, Dwarf_Addr *addr) -{ - if ((state->n = getline (&state->line, &state->linesz, state->f)) < 1 || - state->line[state->n - 2] == ']') - return false; - *addr = strtoull (state->line, &state->p, 16); - state->p += strspn (state->p, " \t"); - state->type = strsep (&state->p, " \t\n"); - if (state->type == NULL) - return false; - return state->p != NULL && state->p != state->line; -} - - -/* Grovel around to guess the bounds of the runtime kernel image. */ -static int -intuit_kernel_bounds (Dwarf_Addr *start, Dwarf_Addr *end, Dwarf_Addr *notes) -{ - struct read_address_state state = { NULL, NULL, 0, 0, NULL, NULL }; - - *notes = 0; - - state.f = fopen (KSYMSFILE, "r"); - if (state.f == NULL) - return errno; - - (void) __fsetlocking (state.f, FSETLOCKING_BYCALLER); - - int result; - do - result = read_address (&state, start) ? 0 : -1; - while (result == 0 && strchr ("TtRr", *state.type) == NULL); - - if (result == 0) - { - *end = *start; - while (read_address (&state, end)) - if (*notes == 0 && !strcmp (state.p, "__start_notes\n")) - *notes = *end; - - Dwarf_Addr round_kernel = sysconf (_SC_PAGESIZE); - *start &= -(Dwarf_Addr) round_kernel; - *end += round_kernel - 1; - *end &= -(Dwarf_Addr) round_kernel; - if (*start >= *end || *end - *start < round_kernel) - result = -1; - } - free (state.line); - - if (result == -1) - result = ferror_unlocked (state.f) ? errno : ENOEXEC; - - fclose (state.f); - - return result; -} - - -/* Look for a build ID note in NOTESFILE and associate the ID with MOD. */ -static int -check_notes (Dwfl_Module *mod, const char *notesfile, - Dwarf_Addr vaddr, const char *secname) -{ - int fd = open (notesfile, O_RDONLY); - if (fd < 0) - return 1; - - assert (sizeof (Elf32_Nhdr) == sizeof (GElf_Nhdr)); - assert (sizeof (Elf64_Nhdr) == sizeof (GElf_Nhdr)); - union - { - GElf_Nhdr nhdr; - unsigned char data[8192]; - } buf; - - ssize_t n = read (fd, buf.data, sizeof buf); - close (fd); - - if (n <= 0) - return 1; - - unsigned char *p = buf.data; - size_t len = 0; - while (p < &buf.data[n]) - { - /* No translation required since we are reading the native kernel. */ - GElf_Nhdr *nhdr = (void *) p; - len += sizeof *nhdr; - p += len; - unsigned char *name = p; - unsigned char *bits; - /* This is somewhat ugly, GNU Property notes use different padding, - but all we have is the file content, so we have to actually check - the name and type. */ - if (nhdr->n_type == NT_GNU_PROPERTY_TYPE_0 - && nhdr->n_namesz == sizeof "GNU" - && name + nhdr->n_namesz < &buf.data[n] - && !memcmp (name, "GNU", sizeof "GNU")) - { - len += nhdr->n_namesz; - len = NOTE_ALIGN8 (len); - p = buf.data + len; - bits = p; - len += nhdr->n_descsz; - len = NOTE_ALIGN8 (len); - p = buf.data + len; - } - else - { - len += nhdr->n_namesz; - len = NOTE_ALIGN4 (len); - p = buf.data + len; - bits = p; - len += nhdr->n_descsz; - len = NOTE_ALIGN4 (len); - p = buf.data + len; - } - - if (p <= &buf.data[n] - && nhdr->n_type == NT_GNU_BUILD_ID - && nhdr->n_namesz == sizeof "GNU" - && !memcmp (name, "GNU", sizeof "GNU")) - { - /* Found it. For a module we must figure out its VADDR now. */ - - if (secname != NULL - && (INTUSE(dwfl_linux_kernel_module_section_address) - (mod, NULL, mod->name, 0, secname, 0, NULL, &vaddr) != 0 - || vaddr == (GElf_Addr) -1l)) - vaddr = 0; - - if (vaddr != 0) - vaddr += bits - buf.data; - return INTUSE(dwfl_module_report_build_id) (mod, bits, - nhdr->n_descsz, vaddr); - } - } - - return 0; -} - -/* Look for a build ID for the kernel. */ -static int -check_kernel_notes (Dwfl_Module *kernelmod, GElf_Addr vaddr) -{ - return check_notes (kernelmod, KNOTESFILE, vaddr, NULL) < 0 ? -1 : 0; -} - -/* Look for a build ID for a loaded kernel module. */ -static int -check_module_notes (Dwfl_Module *mod) -{ - char *dirs[2] = { NULL, NULL }; - if (asprintf (&dirs[0], MODNOTESFMT, mod->name) < 0) - return ENOMEM; - - FTS *fts = fts_open (dirs, FTS_NOSTAT | FTS_LOGICAL, NULL); - if (fts == NULL) - { - free (dirs[0]); - return 0; - } - - int result = 0; - FTSENT *f; - while ((f = fts_read (fts)) != NULL) - { - switch (f->fts_info) - { - case FTS_F: - case FTS_SL: - case FTS_NSOK: - result = check_notes (mod, f->fts_accpath, 0, f->fts_name); - if (result > 0) /* Nothing found. */ - { - result = 0; - continue; - } - break; - - case FTS_ERR: - case FTS_DNR: - result = f->fts_errno; - break; - - case FTS_NS: - case FTS_SLNONE: - default: - continue; - } - - /* We only get here when finished or in error cases. */ - break; - } - fts_close (fts); - free (dirs[0]); - - return result; -} - -int -dwfl_linux_kernel_report_kernel (Dwfl *dwfl) -{ - Dwarf_Addr start = 0; - Dwarf_Addr end = 0; - - #define report() \ - (INTUSE(dwfl_report_module) (dwfl, KERNEL_MODNAME, start, end)) - - /* This is a bit of a kludge. If we already reported the kernel, - don't bother figuring it out again--it never changes. */ - for (Dwfl_Module *m = dwfl->modulelist; m != NULL; m = m->next) - if (!strcmp (m->name, KERNEL_MODNAME)) - { - start = m->low_addr; - end = m->high_addr; - return report () == NULL ? -1 : 0; - } - - /* Try to figure out the bounds of the kernel image without - looking for any vmlinux file. */ - Dwarf_Addr notes; - int result = intuit_kernel_bounds (&start, &end, ¬es); - if (result == 0) - { - Dwfl_Module *mod = report (); - return unlikely (mod == NULL) ? -1 : check_kernel_notes (mod, notes); - } - if (result != ENOENT) - return result; - - /* Find the ELF file for the running kernel and dwfl_report_elf it. */ - return report_kernel (dwfl, NULL, NULL); -} -INTDEF (dwfl_linux_kernel_report_kernel) - - -static inline bool -subst_name (char from, char to, - const char * const module_name, - char * const alternate_name, - const size_t namelen) -{ - const char *n = memchr (module_name, from, namelen); - if (n == NULL) - return false; - char *a = mempcpy (alternate_name, module_name, n - module_name); - *a++ = to; - ++n; - const char *p; - while ((p = memchr (n, from, namelen - (n - module_name))) != NULL) - { - a = mempcpy (a, n, p - n); - *a++ = to; - n = p + 1; - } - memcpy (a, n, namelen - (n - module_name) + 1); - return true; -} - -/* Dwfl_Callbacks.find_elf for the running Linux kernel and its modules. */ - -int -dwfl_linux_kernel_find_elf (Dwfl_Module *mod, - void **userdata __attribute__ ((unused)), - const char *module_name, - Dwarf_Addr base __attribute__ ((unused)), - char **file_name, Elf **elfp) -{ - if (mod->build_id_len > 0) - { - int fd = INTUSE(dwfl_build_id_find_elf) (mod, NULL, NULL, 0, - file_name, elfp); - if (fd >= 0 || mod->main.elf != NULL || errno != 0) - return fd; - } - - const char *release = kernel_release (); - if (release == NULL) - return errno; - - if (!strcmp (module_name, KERNEL_MODNAME)) - return find_kernel_elf (mod->dwfl, release, file_name); - - /* Do "find /lib/modules/`uname -r` -name MODULE_NAME.ko". */ - - char *modulesdir[] = { NULL, NULL }; - if (asprintf (&modulesdir[0], MODULEDIRFMT, release) < 0) - return -1; - - FTS *fts = fts_open (modulesdir, FTS_NOSTAT | FTS_LOGICAL, NULL); - if (fts == NULL) - { - free (modulesdir[0]); - return -1; - } - - size_t namelen = strlen (module_name); - - /* This is a kludge. There is no actual necessary relationship between - the name of the .ko file installed and the module name the kernel - knows it by when it's loaded. The kernel's only idea of the module - name comes from the name embedded in the object's magic - .gnu.linkonce.this_module section. - - In practice, these module names match the .ko file names except for - some using '_' and some using '-'. So our cheap kludge is to look for - two files when either a '_' or '-' appears in a module name, one using - only '_' and one only using '-'. */ - - char *alternate_name = malloc (namelen + 1); - if (unlikely (alternate_name == NULL)) - { - free (modulesdir[0]); - return ENOMEM; - } - if (!subst_name ('-', '_', module_name, alternate_name, namelen) && - !subst_name ('_', '-', module_name, alternate_name, namelen)) - alternate_name[0] = '\0'; - - FTSENT *f; - int error = ENOENT; - while ((f = fts_read (fts)) != NULL) - { - /* Skip a "source" subtree, which tends to be large. - This insane hard-coding of names is what depmod does too. */ - if (f->fts_namelen == sizeof "source" - 1 - && !strcmp (f->fts_name, "source")) - { - fts_set (fts, f, FTS_SKIP); - continue; - } - - error = ENOENT; - switch (f->fts_info) - { - case FTS_F: - case FTS_SL: - case FTS_NSOK: - /* See if this file name is "MODULE_NAME.ko". */ - if (check_suffix (f, namelen) - && (!memcmp (f->fts_name, module_name, namelen) - || !memcmp (f->fts_name, alternate_name, namelen))) - { - int fd = open (f->fts_accpath, O_RDONLY); - *file_name = strdup (f->fts_path); - fts_close (fts); - free (modulesdir[0]); - free (alternate_name); - if (fd < 0) - free (*file_name); - else if (*file_name == NULL) - { - close (fd); - fd = -1; - } - return fd; - } - break; - - case FTS_ERR: - case FTS_DNR: - case FTS_NS: - error = f->fts_errno; - break; - - case FTS_SLNONE: - default: - break; - } - } - - fts_close (fts); - free (modulesdir[0]); - free (alternate_name); - errno = error; - return -1; -} -INTDEF (dwfl_linux_kernel_find_elf) - - -/* Dwfl_Callbacks.section_address for kernel modules in the running Linux. - We read the information from /sys/module directly. */ - -int -dwfl_linux_kernel_module_section_address -(Dwfl_Module *mod __attribute__ ((unused)), - void **userdata __attribute__ ((unused)), - const char *modname, Dwarf_Addr base __attribute__ ((unused)), - const char *secname, Elf32_Word shndx __attribute__ ((unused)), - const GElf_Shdr *shdr __attribute__ ((unused)), - Dwarf_Addr *addr) -{ - char *sysfile; - if (asprintf (&sysfile, SECADDRDIRFMT "%s", modname, secname) < 0) - return DWARF_CB_ABORT; - - FILE *f = fopen (sysfile, "r"); - free (sysfile); - - if (f == NULL) - { - if (errno == ENOENT) - { - /* The .modinfo and .data.percpu sections are never kept - loaded in the kernel. If the kernel was compiled without - CONFIG_MODULE_UNLOAD, the .exit.* sections are not - actually loaded at all. - - Setting *ADDR to -1 tells the caller this section is - actually absent from memory. */ - - if (!strcmp (secname, ".modinfo") - || !strcmp (secname, ".data.percpu") - || !strncmp (secname, ".exit", 5)) - { - *addr = (Dwarf_Addr) -1l; - return DWARF_CB_OK; - } - - /* The goofy PPC64 module_frob_arch_sections function tweaks - the section names as a way to control other kernel code's - behavior, and this cruft leaks out into the /sys information. - The file name for ".init*" may actually look like "_init*". */ - - const bool is_init = !strncmp (secname, ".init", 5); - if (is_init) - { - if (asprintf (&sysfile, SECADDRDIRFMT "_%s", - modname, &secname[1]) < 0) - return ENOMEM; - f = fopen (sysfile, "r"); - free (sysfile); - if (f != NULL) - goto ok; - } - - /* The kernel truncates section names to MODULE_SECT_NAME_LEN - 1. - In case that size increases in the future, look for longer - truncated names first. */ - size_t namelen = strlen (secname); - if (namelen >= MODULE_SECT_NAME_LEN) - { - int len = asprintf (&sysfile, SECADDRDIRFMT "%s", - modname, secname); - if (len < 0) - return DWARF_CB_ABORT; - char *end = sysfile + len; - do - { - *--end = '\0'; - f = fopen (sysfile, "r"); - if (is_init && f == NULL && errno == ENOENT) - { - sysfile[len - namelen] = '_'; - f = fopen (sysfile, "r"); - sysfile[len - namelen] = '.'; - } - } - while (f == NULL && errno == ENOENT - && end - &sysfile[len - namelen] >= MODULE_SECT_NAME_LEN); - free (sysfile); - - if (f != NULL) - goto ok; - } - } - - return DWARF_CB_ABORT; - } - - ok: - (void) __fsetlocking (f, FSETLOCKING_BYCALLER); - - int result = (fscanf (f, "%" PRIx64 "\n", addr) == 1 ? 0 - : ferror_unlocked (f) ? errno : ENOEXEC); - fclose (f); - - if (result == 0) - return DWARF_CB_OK; - - errno = result; - return DWARF_CB_ABORT; -} -INTDEF (dwfl_linux_kernel_module_section_address) - -int -dwfl_linux_kernel_report_modules (Dwfl *dwfl) -{ - FILE *f = fopen (MODULELIST, "r"); - if (f == NULL) - return errno; - - (void) __fsetlocking (f, FSETLOCKING_BYCALLER); - - int result = 0; - Dwarf_Addr modaddr; - unsigned long int modsz; - char modname[128]; - char *line = NULL; - size_t linesz = 0; - /* We can't just use fscanf here because it's not easy to distinguish \n - from other whitespace so as to take the optional word following the - address but always stop at the end of the line. */ - while (getline (&line, &linesz, f) > 0 - && sscanf (line, "%128s %lu %*s %*s %*s %" PRIx64 " %*s\n", - modname, &modsz, &modaddr) == 3) - { - Dwfl_Module *mod = INTUSE(dwfl_report_module) (dwfl, modname, - modaddr, modaddr + modsz); - if (mod == NULL) - { - result = -1; - break; - } - - result = check_module_notes (mod); - } - free (line); - - if (result == 0) - result = ferror_unlocked (f) ? errno : feof_unlocked (f) ? 0 : ENOEXEC; - - fclose (f); - - return result; -} -INTDEF (dwfl_linux_kernel_report_modules) diff --git a/src/elflint.c b/src/elflint.c index fa2533e..684b794 100644 --- a/src/elflint.c +++ b/src/elflint.c @@ -4483,13 +4483,8 @@ only executables, shared objects, and core files can have program headers\n")); continue; } -#ifndef PT_GNU_PROPERTY -#define PT_GNU_PROPERTY (PT_LOOS + 0x474e553) -#endif - if (phdr->p_type >= PT_NUM && phdr->p_type != PT_GNU_EH_FRAME && phdr->p_type != PT_GNU_STACK && phdr->p_type != PT_GNU_RELRO - && phdr->p_type != PT_GNU_PROPERTY /* Check for a known machine-specific type. */ && ebl_segment_type_name (ebl, phdr->p_type, NULL, 0) == NULL) ERROR (gettext ("\ diff --git a/src/elflint.c.pt-gnu-prop b/src/elflint.c.pt-gnu-prop deleted file mode 100644 index 684b794..0000000 --- a/src/elflint.c.pt-gnu-prop +++ /dev/null @@ -1,4790 +0,0 @@ -/* Pedantic checking of ELF files compliance with gABI/psABI spec. - Copyright (C) 2001-2015, 2017, 2018 Red Hat, Inc. - This file is part of elfutils. - Written by Ulrich Drepper , 2001. - - This file is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - elfutils is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include "../libelf/libelfP.h" -#include "../libelf/common.h" -#include "../libebl/libeblP.h" -#include "../libdw/libdwP.h" -#include "../libdwfl/libdwflP.h" -#include "../libdw/memory-access.h" - - -/* Name and version of program. */ -ARGP_PROGRAM_VERSION_HOOK_DEF = print_version; - -/* Bug report address. */ -ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT; - -#define ARGP_strict 300 -#define ARGP_gnuld 301 - -/* Definitions of arguments for argp functions. */ -static const struct argp_option options[] = -{ - { "strict", ARGP_strict, NULL, 0, - N_("Be extremely strict, flag level 2 features."), 0 }, - { "quiet", 'q', NULL, 0, N_("Do not print anything if successful"), 0 }, - { "debuginfo", 'd', NULL, 0, N_("Binary is a separate debuginfo file"), 0 }, - { "gnu-ld", ARGP_gnuld, NULL, 0, - N_("Binary has been created with GNU ld and is therefore known to be \ -broken in certain ways"), 0 }, - { NULL, 0, NULL, 0, NULL, 0 } -}; - -/* Short description of program. */ -static const char doc[] = N_("\ -Pedantic checking of ELF files compliance with gABI/psABI spec."); - -/* Strings for arguments in help texts. */ -static const char args_doc[] = N_("FILE..."); - -/* Prototype for option handler. */ -static error_t parse_opt (int key, char *arg, struct argp_state *state); - -/* Data structure to communicate with argp functions. */ -static struct argp argp = -{ - options, parse_opt, args_doc, doc, NULL, NULL, NULL -}; - - -/* Declarations of local functions. */ -static void process_file (int fd, Elf *elf, const char *prefix, - const char *suffix, const char *fname, size_t size, - bool only_one); -static void process_elf_file (Elf *elf, const char *prefix, const char *suffix, - const char *fname, size_t size, bool only_one); -static void check_note_section (Ebl *ebl, GElf_Ehdr *ehdr, - GElf_Shdr *shdr, int idx); - - -/* Report an error. */ -#define ERROR(str, args...) \ - do { \ - printf (str, ##args); \ - ++error_count; \ - } while (0) -static unsigned int error_count; - -/* True if we should perform very strict testing. */ -static bool be_strict; - -/* True if no message is to be printed if the run is succesful. */ -static bool be_quiet; - -/* True if binary is from strip -f, not a normal ELF file. */ -static bool is_debuginfo; - -/* True if binary is assumed to be generated with GNU ld. */ -static bool gnuld; - -/* Index of section header string table. */ -static uint32_t shstrndx; - -/* Array to count references in section groups. */ -static int *scnref; - -/* Numbers of sections and program headers. */ -static unsigned int shnum; -static unsigned int phnum; - - -int -main (int argc, char *argv[]) -{ - /* Set locale. */ - setlocale (LC_ALL, ""); - - /* Initialize the message catalog. */ - textdomain (PACKAGE_TARNAME); - - /* Parse and process arguments. */ - int remaining; - argp_parse (&argp, argc, argv, 0, &remaining, NULL); - - /* Before we start tell the ELF library which version we are using. */ - elf_version (EV_CURRENT); - - /* Now process all the files given at the command line. */ - bool only_one = remaining + 1 == argc; - do - { - /* Open the file. */ - int fd = open (argv[remaining], O_RDONLY); - if (fd == -1) - { - error (0, errno, _("cannot open input file '%s'"), argv[remaining]); - continue; - } - - /* Create an `Elf' descriptor. */ - Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL); - if (elf == NULL) - ERROR (_("cannot generate Elf descriptor for '%s': %s\n"), - argv[remaining], elf_errmsg (-1)); - else - { - unsigned int prev_error_count = error_count; - struct stat st; - - if (fstat (fd, &st) != 0) - { - printf ("cannot stat '%s': %m\n", argv[remaining]); - close (fd); - continue; - } - - process_file (fd, elf, NULL, NULL, argv[remaining], st.st_size, - only_one); - - /* Now we can close the descriptor. */ - if (elf_end (elf) != 0) - ERROR (gettext ("error while closing Elf descriptor: %s\n"), - elf_errmsg (-1)); - - if (prev_error_count == error_count && !be_quiet) - puts (gettext ("No errors")); - } - - close (fd); - } - while (++remaining < argc); - - return error_count != 0; -} - - -/* Handle program arguments. */ -static error_t -parse_opt (int key, char *arg __attribute__ ((unused)), - struct argp_state *state __attribute__ ((unused))) -{ - switch (key) - { - case ARGP_strict: - be_strict = true; - break; - - case 'q': - be_quiet = true; - break; - - case 'd': - is_debuginfo = true; - break; - - case ARGP_gnuld: - gnuld = true; - break; - - case ARGP_KEY_NO_ARGS: - fputs (gettext ("Missing file name.\n"), stderr); - argp_help (&argp, stderr, ARGP_HELP_SEE, program_invocation_short_name); - exit (EXIT_FAILURE); - - default: - return ARGP_ERR_UNKNOWN; - } - return 0; -} - - -/* Process one file. */ -static void -process_file (int fd, Elf *elf, const char *prefix, const char *suffix, - const char *fname, size_t size, bool only_one) -{ - /* We can handle two types of files: ELF files and archives. */ - Elf_Kind kind = elf_kind (elf); - - switch (kind) - { - case ELF_K_ELF: - /* Yes! It's an ELF file. */ - process_elf_file (elf, prefix, suffix, fname, size, only_one); - break; - - case ELF_K_AR: - { - Elf *subelf; - Elf_Cmd cmd = ELF_C_READ_MMAP; - size_t prefix_len = prefix == NULL ? 0 : strlen (prefix); - size_t fname_len = strlen (fname) + 1; - char new_prefix[prefix_len + 1 + fname_len]; - char new_suffix[(suffix == NULL ? 0 : strlen (suffix)) + 2]; - char *cp = new_prefix; - - /* Create the full name of the file. */ - if (prefix != NULL) - { - cp = mempcpy (cp, prefix, prefix_len); - *cp++ = '('; - strcpy (stpcpy (new_suffix, suffix), ")"); - } - else - new_suffix[0] = '\0'; - memcpy (cp, fname, fname_len); - - /* It's an archive. We process each file in it. */ - while ((subelf = elf_begin (fd, cmd, elf)) != NULL) - { - kind = elf_kind (subelf); - - /* Call this function recursively. */ - if (kind == ELF_K_ELF || kind == ELF_K_AR) - { - Elf_Arhdr *arhdr = elf_getarhdr (subelf); - assert (arhdr != NULL); - - process_file (fd, subelf, new_prefix, new_suffix, - arhdr->ar_name, arhdr->ar_size, false); - } - - /* Get next archive element. */ - cmd = elf_next (subelf); - if (elf_end (subelf) != 0) - ERROR (gettext (" error while freeing sub-ELF descriptor: %s\n"), - elf_errmsg (-1)); - } - } - break; - - default: - /* We cannot do anything. */ - ERROR (gettext ("\ -Not an ELF file - it has the wrong magic bytes at the start\n")); - break; - } -} - - -static const char * -section_name (Ebl *ebl, int idx) -{ - GElf_Shdr shdr_mem; - GElf_Shdr *shdr; - const char *ret; - - if ((unsigned int) idx > shnum) - return ""; - - shdr = gelf_getshdr (elf_getscn (ebl->elf, idx), &shdr_mem); - if (shdr == NULL) - return ""; - - ret = elf_strptr (ebl->elf, shstrndx, shdr->sh_name); - if (ret == NULL) - return ""; - return ret; -} - - -static const int valid_e_machine[] = - { - EM_M32, EM_SPARC, EM_386, EM_68K, EM_88K, EM_860, EM_MIPS, EM_S370, - EM_MIPS_RS3_LE, EM_PARISC, EM_VPP500, EM_SPARC32PLUS, EM_960, EM_PPC, - EM_PPC64, EM_S390, EM_V800, EM_FR20, EM_RH32, EM_RCE, EM_ARM, - EM_FAKE_ALPHA, EM_SH, EM_SPARCV9, EM_TRICORE, EM_ARC, EM_H8_300, - EM_H8_300H, EM_H8S, EM_H8_500, EM_IA_64, EM_MIPS_X, EM_COLDFIRE, - EM_68HC12, EM_MMA, EM_PCP, EM_NCPU, EM_NDR1, EM_STARCORE, EM_ME16, - EM_ST100, EM_TINYJ, EM_X86_64, EM_PDSP, EM_FX66, EM_ST9PLUS, EM_ST7, - EM_68HC16, EM_68HC11, EM_68HC08, EM_68HC05, EM_SVX, EM_ST19, EM_VAX, - EM_CRIS, EM_JAVELIN, EM_FIREPATH, EM_ZSP, EM_MMIX, EM_HUANY, EM_PRISM, - EM_AVR, EM_FR30, EM_D10V, EM_D30V, EM_V850, EM_M32R, EM_MN10300, - EM_MN10200, EM_PJ, EM_OPENRISC, EM_ARC_A5, EM_XTENSA, EM_ALPHA, - EM_TILEGX, EM_TILEPRO, EM_AARCH64, EM_BPF, EM_RISCV, EM_CSKY - }; -#define nvalid_e_machine \ - (sizeof (valid_e_machine) / sizeof (valid_e_machine[0])) - - -static void -check_elf_header (Ebl *ebl, GElf_Ehdr *ehdr, size_t size) -{ - char buf[512]; - size_t cnt; - - /* Check e_ident field. */ - if (ehdr->e_ident[EI_MAG0] != ELFMAG0) - ERROR ("e_ident[%d] != '%c'\n", EI_MAG0, ELFMAG0); - if (ehdr->e_ident[EI_MAG1] != ELFMAG1) - ERROR ("e_ident[%d] != '%c'\n", EI_MAG1, ELFMAG1); - if (ehdr->e_ident[EI_MAG2] != ELFMAG2) - ERROR ("e_ident[%d] != '%c'\n", EI_MAG2, ELFMAG2); - if (ehdr->e_ident[EI_MAG3] != ELFMAG3) - ERROR ("e_ident[%d] != '%c'\n", EI_MAG3, ELFMAG3); - - if (ehdr->e_ident[EI_CLASS] != ELFCLASS32 - && ehdr->e_ident[EI_CLASS] != ELFCLASS64) - ERROR (gettext ("e_ident[%d] == %d is no known class\n"), - EI_CLASS, ehdr->e_ident[EI_CLASS]); - - if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB - && ehdr->e_ident[EI_DATA] != ELFDATA2MSB) - ERROR (gettext ("e_ident[%d] == %d is no known data encoding\n"), - EI_DATA, ehdr->e_ident[EI_DATA]); - - if (ehdr->e_ident[EI_VERSION] != EV_CURRENT) - ERROR (gettext ("unknown ELF header version number e_ident[%d] == %d\n"), - EI_VERSION, ehdr->e_ident[EI_VERSION]); - - /* We currently don't handle any OS ABIs other than Linux and the - kFreeBSD variant of Debian. */ - if (ehdr->e_ident[EI_OSABI] != ELFOSABI_NONE - && ehdr->e_ident[EI_OSABI] != ELFOSABI_LINUX - && ehdr->e_ident[EI_OSABI] != ELFOSABI_FREEBSD) - ERROR (gettext ("unsupported OS ABI e_ident[%d] == '%s'\n"), - EI_OSABI, - ebl_osabi_name (ebl, ehdr->e_ident[EI_OSABI], buf, sizeof (buf))); - - /* No ABI versions other than zero are supported either. */ - if (ehdr->e_ident[EI_ABIVERSION] != 0) - ERROR (gettext ("unsupported ABI version e_ident[%d] == %d\n"), - EI_ABIVERSION, ehdr->e_ident[EI_ABIVERSION]); - - for (cnt = EI_PAD; cnt < EI_NIDENT; ++cnt) - if (ehdr->e_ident[cnt] != 0) - ERROR (gettext ("e_ident[%zu] is not zero\n"), cnt); - - /* Check the e_type field. */ - if (ehdr->e_type != ET_REL && ehdr->e_type != ET_EXEC - && ehdr->e_type != ET_DYN && ehdr->e_type != ET_CORE) - ERROR (gettext ("unknown object file type %d\n"), ehdr->e_type); - - /* Check the e_machine field. */ - for (cnt = 0; cnt < nvalid_e_machine; ++cnt) - if (valid_e_machine[cnt] == ehdr->e_machine) - break; - if (cnt == nvalid_e_machine) - ERROR (gettext ("unknown machine type %d\n"), ehdr->e_machine); - - /* Check the e_version field. */ - if (ehdr->e_version != EV_CURRENT) - ERROR (gettext ("unknown object file version\n")); - - /* Check the e_phoff and e_phnum fields. */ - if (ehdr->e_phoff == 0) - { - if (ehdr->e_phnum != 0) - ERROR (gettext ("invalid program header offset\n")); - else if (ehdr->e_type == ET_EXEC || ehdr->e_type == ET_DYN) - ERROR (gettext ("\ -executables and DSOs cannot have zero program header offset\n")); - } - else if (ehdr->e_phnum == 0) - ERROR (gettext ("invalid number of program header entries\n")); - - /* Check the e_shoff field. */ - shnum = ehdr->e_shnum; - shstrndx = ehdr->e_shstrndx; - if (ehdr->e_shoff == 0) - { - if (ehdr->e_shnum != 0) - ERROR (gettext ("invalid section header table offset\n")); - else if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN - && ehdr->e_type != ET_CORE) - ERROR (gettext ("section header table must be present\n")); - } - else - { - if (ehdr->e_shnum == 0) - { - /* Get the header of the zeroth section. The sh_size field - might contain the section number. */ - GElf_Shdr shdr_mem; - GElf_Shdr *shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem); - if (shdr != NULL) - { - /* The error will be reported later. */ - if (shdr->sh_size == 0) - ERROR (gettext ("\ -invalid number of section header table entries\n")); - else - shnum = shdr->sh_size; - } - } - - if (ehdr->e_shstrndx == SHN_XINDEX) - { - /* Get the header of the zeroth section. The sh_size field - might contain the section number. */ - GElf_Shdr shdr_mem; - GElf_Shdr *shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem); - if (shdr != NULL && shdr->sh_link < shnum) - shstrndx = shdr->sh_link; - } - else if (shstrndx >= shnum) - ERROR (gettext ("invalid section header index\n")); - } - - /* Check the shdrs actually exist. And uncompress them before - further checking. Indexes between sections reference the - uncompressed data. */ - unsigned int scnt; - Elf_Scn *scn = NULL; - for (scnt = 1; scnt < shnum; ++scnt) - { - scn = elf_nextscn (ebl->elf, scn); - if (scn == NULL) - break; - /* If the section wasn't compressed this does nothing, but - returns an error. We don't care. */ - elf_compress (scn, 0, 0); - } - if (scnt < shnum) - ERROR (gettext ("Can only check %u headers, shnum was %u\n"), scnt, shnum); - shnum = scnt; - - phnum = ehdr->e_phnum; - if (ehdr->e_phnum == PN_XNUM) - { - /* Get the header of the zeroth section. The sh_info field - might contain the phnum count. */ - GElf_Shdr shdr_mem; - GElf_Shdr *shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem); - if (shdr != NULL) - { - /* The error will be reported later. */ - if (shdr->sh_info < PN_XNUM) - ERROR (gettext ("\ -invalid number of program header table entries\n")); - else - phnum = shdr->sh_info; - } - } - - /* Check the phdrs actually exist. */ - unsigned int pcnt; - for (pcnt = 0; pcnt < phnum; ++pcnt) - { - GElf_Phdr phdr_mem; - GElf_Phdr *phdr = gelf_getphdr (ebl->elf, pcnt, &phdr_mem); - if (phdr == NULL) - break; - } - if (pcnt < phnum) - ERROR (gettext ("Can only check %u headers, phnum was %u\n"), pcnt, phnum); - phnum = pcnt; - - /* Check the e_flags field. */ - if (!ebl_machine_flag_check (ebl, ehdr->e_flags)) - ERROR (gettext ("invalid machine flags: %s\n"), - ebl_machine_flag_name (ebl, ehdr->e_flags, buf, sizeof (buf))); - - /* Check e_ehsize, e_phentsize, and e_shentsize fields. */ - if (gelf_getclass (ebl->elf) == ELFCLASS32) - { - if (ehdr->e_ehsize != 0 && ehdr->e_ehsize != sizeof (Elf32_Ehdr)) - ERROR (gettext ("invalid ELF header size: %hd\n"), ehdr->e_ehsize); - - if (ehdr->e_phentsize != 0 && ehdr->e_phentsize != sizeof (Elf32_Phdr)) - ERROR (gettext ("invalid program header size: %hd\n"), - ehdr->e_phentsize); - else if (ehdr->e_phoff + phnum * ehdr->e_phentsize > size) - ERROR (gettext ("invalid program header position or size\n")); - - if (ehdr->e_shentsize != 0 && ehdr->e_shentsize != sizeof (Elf32_Shdr)) - ERROR (gettext ("invalid section header size: %hd\n"), - ehdr->e_shentsize); - else if (ehdr->e_shoff + shnum * ehdr->e_shentsize > size) - ERROR (gettext ("invalid section header position or size\n")); - } - else if (gelf_getclass (ebl->elf) == ELFCLASS64) - { - if (ehdr->e_ehsize != 0 && ehdr->e_ehsize != sizeof (Elf64_Ehdr)) - ERROR (gettext ("invalid ELF header size: %hd\n"), ehdr->e_ehsize); - - if (ehdr->e_phentsize != 0 && ehdr->e_phentsize != sizeof (Elf64_Phdr)) - ERROR (gettext ("invalid program header size: %hd\n"), - ehdr->e_phentsize); - else if (ehdr->e_phoff + phnum * ehdr->e_phentsize > size) - ERROR (gettext ("invalid program header position or size\n")); - - if (ehdr->e_shentsize != 0 && ehdr->e_shentsize != sizeof (Elf64_Shdr)) - ERROR (gettext ("invalid section header size: %hd\n"), - ehdr->e_shentsize); - else if (ehdr->e_shoff + shnum * ehdr->e_shentsize > size) - ERROR (gettext ("invalid section header position or size\n")); - } -} - - -/* Check that there is a section group section with index < IDX which - contains section IDX and that there is exactly one. */ -static void -check_scn_group (Ebl *ebl, int idx) -{ - if (scnref[idx] == 0) - { - /* No reference so far. Search following sections, maybe the - order is wrong. */ - size_t cnt; - - for (cnt = idx + 1; cnt < shnum; ++cnt) - { - Elf_Scn *scn = elf_getscn (ebl->elf, cnt); - GElf_Shdr shdr_mem; - GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); - if (shdr == NULL) - /* We cannot get the section header so we cannot check it. - The error to get the section header will be shown - somewhere else. */ - continue; - - if (shdr->sh_type != SHT_GROUP) - continue; - - Elf_Data *data = elf_getdata (scn, NULL); - if (data == NULL || data->d_buf == NULL - || data->d_size < sizeof (Elf32_Word)) - /* Cannot check the section. */ - continue; - - Elf32_Word *grpdata = (Elf32_Word *) data->d_buf; - for (size_t inner = 1; inner < data->d_size / sizeof (Elf32_Word); - ++inner) - if (grpdata[inner] == (Elf32_Word) idx) - goto out; - } - - out: - if (cnt == shnum) - ERROR (gettext ("\ -section [%2d] '%s': section with SHF_GROUP flag set not part of a section group\n"), - idx, section_name (ebl, idx)); - else - ERROR (gettext ("\ -section [%2d] '%s': section group [%2zu] '%s' does not precede group member\n"), - idx, section_name (ebl, idx), - cnt, section_name (ebl, cnt)); - } -} - - -static void -check_symtab (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) -{ - bool no_xndx_warned = false; - int no_pt_tls = 0; - Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL); - if (data == NULL) - { - ERROR (gettext ("section [%2d] '%s': cannot get section data\n"), - idx, section_name (ebl, idx)); - return; - } - - GElf_Shdr strshdr_mem; - GElf_Shdr *strshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), - &strshdr_mem); - if (strshdr == NULL) - return; - - if (strshdr->sh_type != SHT_STRTAB) - { - ERROR (gettext ("section [%2d] '%s': referenced as string table for section [%2d] '%s' but type is not SHT_STRTAB\n"), - shdr->sh_link, section_name (ebl, shdr->sh_link), - idx, section_name (ebl, idx)); - strshdr = NULL; - } - - /* Search for an extended section index table section. */ - Elf_Data *xndxdata = NULL; - Elf32_Word xndxscnidx = 0; - bool found_xndx = false; - for (size_t cnt = 1; cnt < shnum; ++cnt) - if (cnt != (size_t) idx) - { - Elf_Scn *xndxscn = elf_getscn (ebl->elf, cnt); - GElf_Shdr xndxshdr_mem; - GElf_Shdr *xndxshdr = gelf_getshdr (xndxscn, &xndxshdr_mem); - if (xndxshdr == NULL) - continue; - - if (xndxshdr->sh_type == SHT_SYMTAB_SHNDX - && xndxshdr->sh_link == (GElf_Word) idx) - { - if (found_xndx) - ERROR (gettext ("\ -section [%2d] '%s': symbol table cannot have more than one extended index section\n"), - idx, section_name (ebl, idx)); - - xndxdata = elf_getdata (xndxscn, NULL); - xndxscnidx = elf_ndxscn (xndxscn); - found_xndx = true; - } - } - - size_t sh_entsize = gelf_fsize (ebl->elf, ELF_T_SYM, 1, EV_CURRENT); - if (shdr->sh_entsize != sh_entsize) - ERROR (gettext ("\ -section [%2u] '%s': entry size is does not match ElfXX_Sym\n"), - idx, section_name (ebl, idx)); - - /* Test the zeroth entry. */ - GElf_Sym sym_mem; - Elf32_Word xndx; - GElf_Sym *sym = gelf_getsymshndx (data, xndxdata, 0, &sym_mem, &xndx); - if (sym == NULL) - ERROR (gettext ("section [%2d] '%s': cannot get symbol %d: %s\n"), - idx, section_name (ebl, idx), 0, elf_errmsg (-1)); - else - { - if (sym->st_name != 0) - ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"), - idx, section_name (ebl, idx), "st_name"); - if (sym->st_value != 0) - ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"), - idx, section_name (ebl, idx), "st_value"); - if (sym->st_size != 0) - ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"), - idx, section_name (ebl, idx), "st_size"); - if (sym->st_info != 0) - ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"), - idx, section_name (ebl, idx), "st_info"); - if (sym->st_other != 0) - ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"), - idx, section_name (ebl, idx), "st_other"); - if (sym->st_shndx != 0) - ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"), - idx, section_name (ebl, idx), "st_shndx"); - if (xndxdata != NULL && xndx != 0) - ERROR (gettext ("\ -section [%2d] '%s': XINDEX for zeroth entry not zero\n"), - xndxscnidx, section_name (ebl, xndxscnidx)); - } - - for (size_t cnt = 1; cnt < shdr->sh_size / sh_entsize; ++cnt) - { - sym = gelf_getsymshndx (data, xndxdata, cnt, &sym_mem, &xndx); - if (sym == NULL) - { - ERROR (gettext ("section [%2d] '%s': cannot get symbol %zu: %s\n"), - idx, section_name (ebl, idx), cnt, elf_errmsg (-1)); - continue; - } - - const char *name = NULL; - if (strshdr == NULL) - name = ""; - else if (sym->st_name >= strshdr->sh_size) - ERROR (gettext ("\ -section [%2d] '%s': symbol %zu: invalid name value\n"), - idx, section_name (ebl, idx), cnt); - else - { - name = elf_strptr (ebl->elf, shdr->sh_link, sym->st_name); - if (name == NULL) - name = ""; - } - - if (sym->st_shndx == SHN_XINDEX) - { - if (xndxdata == NULL) - { - if (!no_xndx_warned) - ERROR (gettext ("\ -section [%2d] '%s': symbol %zu: too large section index but no extended section index section\n"), - idx, section_name (ebl, idx), cnt); - no_xndx_warned = true; - } - else if (xndx < SHN_LORESERVE) - ERROR (gettext ("\ -section [%2d] '%s': symbol %zu: XINDEX used for index which would fit in st_shndx (%" PRIu32 ")\n"), - xndxscnidx, section_name (ebl, xndxscnidx), cnt, - xndx); - } - else if ((sym->st_shndx >= SHN_LORESERVE - // && sym->st_shndx <= SHN_HIRESERVE always true - && sym->st_shndx != SHN_ABS - && sym->st_shndx != SHN_COMMON) - || (sym->st_shndx >= shnum - && (sym->st_shndx < SHN_LORESERVE - /* || sym->st_shndx > SHN_HIRESERVE always false */))) - ERROR (gettext ("\ -section [%2d] '%s': symbol %zu: invalid section index\n"), - idx, section_name (ebl, idx), cnt); - else - xndx = sym->st_shndx; - - if (GELF_ST_TYPE (sym->st_info) >= STT_NUM - && !ebl_symbol_type_name (ebl, GELF_ST_TYPE (sym->st_info), NULL, 0)) - ERROR (gettext ("section [%2d] '%s': symbol %zu: unknown type\n"), - idx, section_name (ebl, idx), cnt); - - if (GELF_ST_BIND (sym->st_info) >= STB_NUM - && !ebl_symbol_binding_name (ebl, GELF_ST_BIND (sym->st_info), NULL, - 0)) - ERROR (gettext ("\ -section [%2d] '%s': symbol %zu: unknown symbol binding\n"), - idx, section_name (ebl, idx), cnt); - if (GELF_ST_BIND (sym->st_info) == STB_GNU_UNIQUE - && GELF_ST_TYPE (sym->st_info) != STT_OBJECT) - ERROR (gettext ("\ -section [%2d] '%s': symbol %zu: unique symbol not of object type\n"), - idx, section_name (ebl, idx), cnt); - - if (xndx == SHN_COMMON) - { - /* Common symbols can only appear in relocatable files. */ - if (ehdr->e_type != ET_REL) - ERROR (gettext ("\ -section [%2d] '%s': symbol %zu: COMMON only allowed in relocatable files\n"), - idx, section_name (ebl, idx), cnt); - if (cnt < shdr->sh_info) - ERROR (gettext ("\ -section [%2d] '%s': symbol %zu: local COMMON symbols are nonsense\n"), - idx, section_name (ebl, idx), cnt); - if (GELF_R_TYPE (sym->st_info) == STT_FUNC) - ERROR (gettext ("\ -section [%2d] '%s': symbol %zu: function in COMMON section is nonsense\n"), - idx, section_name (ebl, idx), cnt); - } - else if (xndx > 0 && xndx < shnum) - { - GElf_Shdr destshdr_mem; - GElf_Shdr *destshdr; - - destshdr = gelf_getshdr (elf_getscn (ebl->elf, xndx), &destshdr_mem); - if (destshdr != NULL) - { - GElf_Addr sh_addr = (ehdr->e_type == ET_REL ? 0 - : destshdr->sh_addr); - GElf_Addr st_value; - if (GELF_ST_TYPE (sym->st_info) == STT_FUNC - || (GELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)) - st_value = sym->st_value & ebl_func_addr_mask (ebl); - else - st_value = sym->st_value; - if (GELF_ST_TYPE (sym->st_info) != STT_TLS) - { - if (! ebl_check_special_symbol (ebl, sym, name, - destshdr)) - { - if (st_value - sh_addr > destshdr->sh_size) - { - /* GNU ld has severe bugs. When it decides to remove - empty sections it leaves symbols referencing them - behind. These are symbols in .symtab or .dynsym - and for the named symbols have zero size. See - sourceware PR13621. */ - if (!gnuld - || (strcmp (section_name (ebl, idx), ".symtab") - && strcmp (section_name (ebl, idx), - ".dynsym")) - || sym->st_size != 0 - || (strcmp (name, "__preinit_array_start") != 0 - && strcmp (name, "__preinit_array_end") != 0 - && strcmp (name, "__init_array_start") != 0 - && strcmp (name, "__init_array_end") != 0 - && strcmp (name, "__fini_array_start") != 0 - && strcmp (name, "__fini_array_end") != 0 - && strcmp (name, "__bss_start") != 0 - && strcmp (name, "__bss_start__") != 0 - && strcmp (name, "__TMC_END__") != 0 - && strcmp (name, ".TOC.") != 0 - && strcmp (name, "_edata") != 0 - && strcmp (name, "__edata") != 0 - && strcmp (name, "_end") != 0 - && strcmp (name, "__end") != 0)) - ERROR (gettext ("\ -section [%2d] '%s': symbol %zu: st_value out of bounds\n"), - idx, section_name (ebl, idx), cnt); - } - else if ((st_value - sh_addr - + sym->st_size) > destshdr->sh_size) - ERROR (gettext ("\ -section [%2d] '%s': symbol %zu does not fit completely in referenced section [%2d] '%s'\n"), - idx, section_name (ebl, idx), cnt, - (int) xndx, section_name (ebl, xndx)); - } - } - else - { - if ((destshdr->sh_flags & SHF_TLS) == 0) - ERROR (gettext ("\ -section [%2d] '%s': symbol %zu: referenced section [%2d] '%s' does not have SHF_TLS flag set\n"), - idx, section_name (ebl, idx), cnt, - (int) xndx, section_name (ebl, xndx)); - - if (ehdr->e_type == ET_REL) - { - /* For object files the symbol value must fall - into the section. */ - if (st_value > destshdr->sh_size) - ERROR (gettext ("\ -section [%2d] '%s': symbol %zu: st_value out of bounds of referenced section [%2d] '%s'\n"), - idx, section_name (ebl, idx), cnt, - (int) xndx, section_name (ebl, xndx)); - else if (st_value + sym->st_size - > destshdr->sh_size) - ERROR (gettext ("\ -section [%2d] '%s': symbol %zu does not fit completely in referenced section [%2d] '%s'\n"), - idx, section_name (ebl, idx), cnt, - (int) xndx, section_name (ebl, xndx)); - } - else - { - GElf_Phdr phdr_mem; - GElf_Phdr *phdr = NULL; - unsigned int pcnt; - - for (pcnt = 0; pcnt < phnum; ++pcnt) - { - phdr = gelf_getphdr (ebl->elf, pcnt, &phdr_mem); - if (phdr != NULL && phdr->p_type == PT_TLS) - break; - } - - if (pcnt == phnum) - { - if (no_pt_tls++ == 0) - ERROR (gettext ("\ -section [%2d] '%s': symbol %zu: TLS symbol but no TLS program header entry\n"), - idx, section_name (ebl, idx), cnt); - } - else if (phdr == NULL) - { - ERROR (gettext ("\ -section [%2d] '%s': symbol %zu: TLS symbol but couldn't get TLS program header entry\n"), - idx, section_name (ebl, idx), cnt); - } - else if (!is_debuginfo) - { - if (st_value - < destshdr->sh_offset - phdr->p_offset) - ERROR (gettext ("\ -section [%2d] '%s': symbol %zu: st_value short of referenced section [%2d] '%s'\n"), - idx, section_name (ebl, idx), cnt, - (int) xndx, section_name (ebl, xndx)); - else if (st_value - > (destshdr->sh_offset - phdr->p_offset - + destshdr->sh_size)) - ERROR (gettext ("\ -section [%2d] '%s': symbol %zu: st_value out of bounds of referenced section [%2d] '%s'\n"), - idx, section_name (ebl, idx), cnt, - (int) xndx, section_name (ebl, xndx)); - else if (st_value + sym->st_size - > (destshdr->sh_offset - phdr->p_offset - + destshdr->sh_size)) - ERROR (gettext ("\ -section [%2d] '%s': symbol %zu does not fit completely in referenced section [%2d] '%s'\n"), - idx, section_name (ebl, idx), cnt, - (int) xndx, section_name (ebl, xndx)); - } - } - } - } - } - - if (GELF_ST_BIND (sym->st_info) == STB_LOCAL) - { - if (cnt >= shdr->sh_info) - ERROR (gettext ("\ -section [%2d] '%s': symbol %zu: local symbol outside range described in sh_info\n"), - idx, section_name (ebl, idx), cnt); - } - else - { - if (cnt < shdr->sh_info) - ERROR (gettext ("\ -section [%2d] '%s': symbol %zu: non-local symbol outside range described in sh_info\n"), - idx, section_name (ebl, idx), cnt); - } - - if (GELF_ST_TYPE (sym->st_info) == STT_SECTION - && GELF_ST_BIND (sym->st_info) != STB_LOCAL) - ERROR (gettext ("\ -section [%2d] '%s': symbol %zu: non-local section symbol\n"), - idx, section_name (ebl, idx), cnt); - - if (name != NULL) - { - if (strcmp (name, "_GLOBAL_OFFSET_TABLE_") == 0) - { - /* Check that address and size match the global offset table. */ - - GElf_Shdr destshdr_mem; - GElf_Shdr *destshdr = gelf_getshdr (elf_getscn (ebl->elf, xndx), - &destshdr_mem); - - if (destshdr == NULL && xndx == SHN_ABS) - { - /* In a DSO, we have to find the GOT section by name. */ - Elf_Scn *gotscn = NULL; - Elf_Scn *gscn = NULL; - while ((gscn = elf_nextscn (ebl->elf, gscn)) != NULL) - { - destshdr = gelf_getshdr (gscn, &destshdr_mem); - assert (destshdr != NULL); - const char *sname = elf_strptr (ebl->elf, - shstrndx, - destshdr->sh_name); - if (sname != NULL) - { - if (strcmp (sname, ".got.plt") == 0) - break; - if (strcmp (sname, ".got") == 0) - /* Do not stop looking. - There might be a .got.plt section. */ - gotscn = gscn; - } - - destshdr = NULL; - } - - if (destshdr == NULL && gotscn != NULL) - destshdr = gelf_getshdr (gotscn, &destshdr_mem); - } - - const char *sname = ((destshdr == NULL || xndx == SHN_UNDEF) - ? NULL - : elf_strptr (ebl->elf, shstrndx, - destshdr->sh_name)); - if (sname == NULL) - { - if (xndx != SHN_UNDEF || ehdr->e_type != ET_REL) - ERROR (gettext ("\ -section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol refers to \ -bad section [%2d]\n"), - idx, section_name (ebl, idx), xndx); - } - else if (strcmp (sname, ".got.plt") != 0 - && strcmp (sname, ".got") != 0) - ERROR (gettext ("\ -section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol refers to \ -section [%2d] '%s'\n"), - idx, section_name (ebl, idx), xndx, sname); - - if (destshdr != NULL) - { - /* Found it. */ - if (!ebl_check_special_symbol (ebl, sym, name, - destshdr)) - { - if (ehdr->e_type != ET_REL - && sym->st_value != destshdr->sh_addr) - /* This test is more strict than the psABIs which - usually allow the symbol to be in the middle of - the .got section, allowing negative offsets. */ - ERROR (gettext ("\ -section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol value %#" PRIx64 " does not match %s section address %#" PRIx64 "\n"), - idx, section_name (ebl, idx), - (uint64_t) sym->st_value, - sname, (uint64_t) destshdr->sh_addr); - - if (!gnuld && sym->st_size != destshdr->sh_size) - ERROR (gettext ("\ -section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol size %" PRIu64 " does not match %s section size %" PRIu64 "\n"), - idx, section_name (ebl, idx), - (uint64_t) sym->st_size, - sname, (uint64_t) destshdr->sh_size); - } - } - else - ERROR (gettext ("\ -section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol present, but no .got section\n"), - idx, section_name (ebl, idx)); - } - else if (strcmp (name, "_DYNAMIC") == 0) - /* Check that address and size match the dynamic section. - We locate the dynamic section via the program header - entry. */ - for (unsigned int pcnt = 0; pcnt < phnum; ++pcnt) - { - GElf_Phdr phdr_mem; - GElf_Phdr *phdr = gelf_getphdr (ebl->elf, pcnt, &phdr_mem); - - if (phdr != NULL && phdr->p_type == PT_DYNAMIC) - { - if (sym->st_value != phdr->p_vaddr) - ERROR (gettext ("\ -section [%2d] '%s': _DYNAMIC_ symbol value %#" PRIx64 " does not match dynamic segment address %#" PRIx64 "\n"), - idx, section_name (ebl, idx), - (uint64_t) sym->st_value, - (uint64_t) phdr->p_vaddr); - - if (!gnuld && sym->st_size != phdr->p_memsz) - ERROR (gettext ("\ -section [%2d] '%s': _DYNAMIC symbol size %" PRIu64 " does not match dynamic segment size %" PRIu64 "\n"), - idx, section_name (ebl, idx), - (uint64_t) sym->st_size, - (uint64_t) phdr->p_memsz); - - break; - } - } - } - - if (GELF_ST_VISIBILITY (sym->st_other) != STV_DEFAULT - && shdr->sh_type == SHT_DYNSYM) - ERROR (gettext ("\ -section [%2d] '%s': symbol %zu: symbol in dynamic symbol table with non-default visibility\n"), - idx, section_name (ebl, idx), cnt); - if (! ebl_check_st_other_bits (ebl, sym->st_other)) - ERROR (gettext ("\ -section [%2d] '%s': symbol %zu: unknown bit set in st_other\n"), - idx, section_name (ebl, idx), cnt); - - } -} - - -static bool -is_rel_dyn (Ebl *ebl, const GElf_Ehdr *ehdr, int idx, const GElf_Shdr *shdr, - bool is_rela) -{ - /* If this is no executable or DSO it cannot be a .rel.dyn section. */ - if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) - return false; - - /* Check the section name. Unfortunately necessary. */ - if (strcmp (section_name (ebl, idx), is_rela ? ".rela.dyn" : ".rel.dyn")) - return false; - - /* When a .rel.dyn section is used a DT_RELCOUNT dynamic section - entry can be present as well. */ - Elf_Scn *scn = NULL; - while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) - { - GElf_Shdr rcshdr_mem; - const GElf_Shdr *rcshdr = gelf_getshdr (scn, &rcshdr_mem); - - if (rcshdr == NULL) - break; - - if (rcshdr->sh_type == SHT_DYNAMIC && rcshdr->sh_entsize != 0) - { - /* Found the dynamic section. Look through it. */ - Elf_Data *d = elf_getdata (scn, NULL); - size_t cnt; - - if (d == NULL) - ERROR (gettext ("\ -section [%2d] '%s': cannot get section data.\n"), - idx, section_name (ebl, idx)); - - for (cnt = 1; cnt < rcshdr->sh_size / rcshdr->sh_entsize; ++cnt) - { - GElf_Dyn dyn_mem; - GElf_Dyn *dyn = gelf_getdyn (d, cnt, &dyn_mem); - - if (dyn == NULL) - break; - - if (dyn->d_tag == DT_RELCOUNT) - { - /* Found it. Does the type match. */ - if (is_rela) - ERROR (gettext ("\ -section [%2d] '%s': DT_RELCOUNT used for this RELA section\n"), - idx, section_name (ebl, idx)); - else - { - /* Does the number specified number of relative - relocations exceed the total number of - relocations? */ - if (shdr->sh_entsize != 0 - && dyn->d_un.d_val > (shdr->sh_size - / shdr->sh_entsize)) - ERROR (gettext ("\ -section [%2d] '%s': DT_RELCOUNT value %d too high for this section\n"), - idx, section_name (ebl, idx), - (int) dyn->d_un.d_val); - - /* Make sure the specified number of relocations are - relative. */ - Elf_Data *reldata = elf_getdata (elf_getscn (ebl->elf, - idx), NULL); - if (reldata != NULL && shdr->sh_entsize != 0) - for (size_t inner = 0; - inner < shdr->sh_size / shdr->sh_entsize; - ++inner) - { - GElf_Rel rel_mem; - GElf_Rel *rel = gelf_getrel (reldata, inner, - &rel_mem); - if (rel == NULL) - /* The problem will be reported elsewhere. */ - break; - - if (ebl_relative_reloc_p (ebl, - GELF_R_TYPE (rel->r_info))) - { - if (inner >= dyn->d_un.d_val) - ERROR (gettext ("\ -section [%2d] '%s': relative relocations after index %d as specified by DT_RELCOUNT\n"), - idx, section_name (ebl, idx), - (int) dyn->d_un.d_val); - } - else if (inner < dyn->d_un.d_val) - ERROR (gettext ("\ -section [%2d] '%s': non-relative relocation at index %zu; DT_RELCOUNT specified %d relative relocations\n"), - idx, section_name (ebl, idx), - inner, (int) dyn->d_un.d_val); - } - } - } - - if (dyn->d_tag == DT_RELACOUNT) - { - /* Found it. Does the type match. */ - if (!is_rela) - ERROR (gettext ("\ -section [%2d] '%s': DT_RELACOUNT used for this REL section\n"), - idx, section_name (ebl, idx)); - else - { - /* Does the number specified number of relative - relocations exceed the total number of - relocations? */ - if (shdr->sh_entsize != 0 - && dyn->d_un.d_val > shdr->sh_size / shdr->sh_entsize) - ERROR (gettext ("\ -section [%2d] '%s': DT_RELCOUNT value %d too high for this section\n"), - idx, section_name (ebl, idx), - (int) dyn->d_un.d_val); - - /* Make sure the specified number of relocations are - relative. */ - Elf_Data *reldata = elf_getdata (elf_getscn (ebl->elf, - idx), NULL); - if (reldata != NULL && shdr->sh_entsize != 0) - for (size_t inner = 0; - inner < shdr->sh_size / shdr->sh_entsize; - ++inner) - { - GElf_Rela rela_mem; - GElf_Rela *rela = gelf_getrela (reldata, inner, - &rela_mem); - if (rela == NULL) - /* The problem will be reported elsewhere. */ - break; - - if (ebl_relative_reloc_p (ebl, - GELF_R_TYPE (rela->r_info))) - { - if (inner >= dyn->d_un.d_val) - ERROR (gettext ("\ -section [%2d] '%s': relative relocations after index %d as specified by DT_RELCOUNT\n"), - idx, section_name (ebl, idx), - (int) dyn->d_un.d_val); - } - else if (inner < dyn->d_un.d_val) - ERROR (gettext ("\ -section [%2d] '%s': non-relative relocation at index %zu; DT_RELCOUNT specified %d relative relocations\n"), - idx, section_name (ebl, idx), - inner, (int) dyn->d_un.d_val); - } - } - } - } - - break; - } - } - - return true; -} - - -struct loaded_segment -{ - GElf_Addr from; - GElf_Addr to; - bool read_only; - struct loaded_segment *next; -}; - - -/* Check whether binary has text relocation flag set. */ -static bool textrel; - -/* Keep track of whether text relocation flag is needed. */ -static bool needed_textrel; - - -static bool -check_reloc_shdr (Ebl *ebl, const GElf_Ehdr *ehdr, const GElf_Shdr *shdr, - int idx, int reltype, GElf_Shdr **destshdrp, - GElf_Shdr *destshdr_memp, struct loaded_segment **loadedp) -{ - bool reldyn = false; - - /* Check whether the link to the section we relocate is reasonable. */ - if (shdr->sh_info >= shnum) - ERROR (gettext ("section [%2d] '%s': invalid destination section index\n"), - idx, section_name (ebl, idx)); - else if (shdr->sh_info != 0) - { - *destshdrp = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_info), - destshdr_memp); - if (*destshdrp != NULL) - { - if(! ebl_check_reloc_target_type (ebl, (*destshdrp)->sh_type)) - { - reldyn = is_rel_dyn (ebl, ehdr, idx, shdr, true); - if (!reldyn) - ERROR (gettext ("\ -section [%2d] '%s': invalid destination section type\n"), - idx, section_name (ebl, idx)); - else - { - /* There is no standard, but we require that .rel{,a}.dyn - sections have a sh_info value of zero. */ - if (shdr->sh_info != 0) - ERROR (gettext ("\ -section [%2d] '%s': sh_info should be zero\n"), - idx, section_name (ebl, idx)); - } - } - - if ((((*destshdrp)->sh_flags & SHF_MERGE) != 0) - && ((*destshdrp)->sh_flags & SHF_STRINGS) != 0) - ERROR (gettext ("\ -section [%2d] '%s': no relocations for merge-able string sections possible\n"), - idx, section_name (ebl, idx)); - } - } - - size_t sh_entsize = gelf_fsize (ebl->elf, reltype, 1, EV_CURRENT); - if (shdr->sh_entsize != sh_entsize) - ERROR (gettext (reltype == ELF_T_RELA ? "\ -section [%2d] '%s': section entry size does not match ElfXX_Rela\n" : "\ -section [%2d] '%s': section entry size does not match ElfXX_Rel\n"), - idx, section_name (ebl, idx)); - - /* In preparation of checking whether relocations are text - relocations or not we need to determine whether the file is - flagged to have text relocation and we need to determine a) what - the loaded segments are and b) which are read-only. This will - also allow us to determine whether the same reloc section is - modifying loaded and not loaded segments. */ - for (unsigned int i = 0; i < phnum; ++i) - { - GElf_Phdr phdr_mem; - GElf_Phdr *phdr = gelf_getphdr (ebl->elf, i, &phdr_mem); - if (phdr == NULL) - continue; - - if (phdr->p_type == PT_LOAD) - { - struct loaded_segment *newp = xmalloc (sizeof (*newp)); - newp->from = phdr->p_vaddr; - newp->to = phdr->p_vaddr + phdr->p_memsz; - newp->read_only = (phdr->p_flags & PF_W) == 0; - newp->next = *loadedp; - *loadedp = newp; - } - else if (phdr->p_type == PT_DYNAMIC) - { - Elf_Scn *dynscn = gelf_offscn (ebl->elf, phdr->p_offset); - GElf_Shdr dynshdr_mem; - GElf_Shdr *dynshdr = gelf_getshdr (dynscn, &dynshdr_mem); - Elf_Data *dyndata = elf_getdata (dynscn, NULL); - if (dynshdr != NULL && dynshdr->sh_type == SHT_DYNAMIC - && dyndata != NULL && dynshdr->sh_entsize != 0) - for (size_t j = 0; j < dynshdr->sh_size / dynshdr->sh_entsize; ++j) - { - GElf_Dyn dyn_mem; - GElf_Dyn *dyn = gelf_getdyn (dyndata, j, &dyn_mem); - if (dyn != NULL - && (dyn->d_tag == DT_TEXTREL - || (dyn->d_tag == DT_FLAGS - && (dyn->d_un.d_val & DF_TEXTREL) != 0))) - { - textrel = true; - break; - } - } - } - } - - /* A quick test which can be easily done here (although it is a bit - out of place): the text relocation flag makes only sense if there - is a segment which is not writable. */ - if (textrel) - { - struct loaded_segment *seg = *loadedp; - while (seg != NULL && !seg->read_only) - seg = seg->next; - if (seg == NULL) - ERROR (gettext ("\ -text relocation flag set but there is no read-only segment\n")); - } - - return reldyn; -} - - -enum load_state - { - state_undecided, - state_loaded, - state_unloaded, - state_error - }; - - -static void -check_one_reloc (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *relshdr, int idx, - size_t cnt, const GElf_Shdr *symshdr, Elf_Data *symdata, - GElf_Addr r_offset, GElf_Xword r_info, - const GElf_Shdr *destshdr, bool reldyn, - struct loaded_segment *loaded, enum load_state *statep) -{ - bool known_broken = gnuld; - - if (!ebl_reloc_type_check (ebl, GELF_R_TYPE (r_info))) - ERROR (gettext ("section [%2d] '%s': relocation %zu: invalid type\n"), - idx, section_name (ebl, idx), cnt); - else if (((ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) - /* The executable/DSO can contain relocation sections with - all the relocations the linker has applied. Those sections - are marked non-loaded, though. */ - || (relshdr->sh_flags & SHF_ALLOC) != 0) - && !ebl_reloc_valid_use (ebl, GELF_R_TYPE (r_info))) - ERROR (gettext ("\ -section [%2d] '%s': relocation %zu: relocation type invalid for the file type\n"), - idx, section_name (ebl, idx), cnt); - - if (symshdr != NULL - && ((GELF_R_SYM (r_info) + 1) - * gelf_fsize (ebl->elf, ELF_T_SYM, 1, EV_CURRENT) - > symshdr->sh_size)) - ERROR (gettext ("\ -section [%2d] '%s': relocation %zu: invalid symbol index\n"), - idx, section_name (ebl, idx), cnt); - - /* No more tests if this is a no-op relocation. */ - if (ebl_none_reloc_p (ebl, GELF_R_TYPE (r_info))) - return; - - if (ebl_gotpc_reloc_check (ebl, GELF_R_TYPE (r_info))) - { - const char *name; - char buf[64]; - GElf_Sym sym_mem; - GElf_Sym *sym = gelf_getsym (symdata, GELF_R_SYM (r_info), &sym_mem); - if (sym != NULL - /* Get the name for the symbol. */ - && (name = elf_strptr (ebl->elf, symshdr->sh_link, sym->st_name)) - && strcmp (name, "_GLOBAL_OFFSET_TABLE_") !=0 ) - ERROR (gettext ("\ -section [%2d] '%s': relocation %zu: only symbol '_GLOBAL_OFFSET_TABLE_' can be used with %s\n"), - idx, section_name (ebl, idx), cnt, - ebl_reloc_type_name (ebl, GELF_R_SYM (r_info), - buf, sizeof (buf))); - } - - if (reldyn) - { - // XXX TODO Check .rel.dyn section addresses. - } - else if (!known_broken) - { - if (destshdr != NULL - && GELF_R_TYPE (r_info) != 0 - && (r_offset - (ehdr->e_type == ET_REL ? 0 - : destshdr->sh_addr)) >= destshdr->sh_size) - ERROR (gettext ("\ -section [%2d] '%s': relocation %zu: offset out of bounds\n"), - idx, section_name (ebl, idx), cnt); - } - - GElf_Sym sym_mem; - GElf_Sym *sym = gelf_getsym (symdata, GELF_R_SYM (r_info), &sym_mem); - - if (ebl_copy_reloc_p (ebl, GELF_R_TYPE (r_info)) - /* Make sure the referenced symbol is an object or unspecified. */ - && sym != NULL - && GELF_ST_TYPE (sym->st_info) != STT_NOTYPE - && GELF_ST_TYPE (sym->st_info) != STT_OBJECT) - { - char buf[64]; - ERROR (gettext ("section [%2d] '%s': relocation %zu: copy relocation against symbol of type %s\n"), - idx, section_name (ebl, idx), cnt, - ebl_symbol_type_name (ebl, GELF_ST_TYPE (sym->st_info), - buf, sizeof (buf))); - } - - if ((ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) - || (relshdr->sh_flags & SHF_ALLOC) != 0) - { - bool in_loaded_seg = false; - while (loaded != NULL) - { - if (r_offset < loaded->to - && r_offset + (sym == NULL ? 0 : sym->st_size) >= loaded->from) - { - /* The symbol is in this segment. */ - if (loaded->read_only) - { - if (textrel) - needed_textrel = true; - else - ERROR (gettext ("section [%2d] '%s': relocation %zu: read-only section modified but text relocation flag not set\n"), - idx, section_name (ebl, idx), cnt); - } - - in_loaded_seg = true; - } - - loaded = loaded->next; - } - - if (*statep == state_undecided) - *statep = in_loaded_seg ? state_loaded : state_unloaded; - else if ((*statep == state_unloaded && in_loaded_seg) - || (*statep == state_loaded && !in_loaded_seg)) - { - ERROR (gettext ("\ -section [%2d] '%s': relocations are against loaded and unloaded data\n"), - idx, section_name (ebl, idx)); - *statep = state_error; - } - } -} - - -static void -check_rela (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) -{ - Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL); - if (data == NULL) - { - ERROR (gettext ("section [%2d] '%s': cannot get section data\n"), - idx, section_name (ebl, idx)); - return; - } - - /* Check the fields of the section header. */ - GElf_Shdr destshdr_mem; - GElf_Shdr *destshdr = NULL; - struct loaded_segment *loaded = NULL; - bool reldyn = check_reloc_shdr (ebl, ehdr, shdr, idx, ELF_T_RELA, &destshdr, - &destshdr_mem, &loaded); - - Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link); - GElf_Shdr symshdr_mem; - GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem); - Elf_Data *symdata = elf_getdata (symscn, NULL); - enum load_state state = state_undecided; - - size_t sh_entsize = gelf_fsize (ebl->elf, ELF_T_RELA, 1, EV_CURRENT); - for (size_t cnt = 0; cnt < shdr->sh_size / sh_entsize; ++cnt) - { - GElf_Rela rela_mem; - GElf_Rela *rela = gelf_getrela (data, cnt, &rela_mem); - if (rela == NULL) - { - ERROR (gettext ("\ -section [%2d] '%s': cannot get relocation %zu: %s\n"), - idx, section_name (ebl, idx), cnt, elf_errmsg (-1)); - continue; - } - - check_one_reloc (ebl, ehdr, shdr, idx, cnt, symshdr, symdata, - rela->r_offset, rela->r_info, destshdr, reldyn, loaded, - &state); - } - - while (loaded != NULL) - { - struct loaded_segment *old = loaded; - loaded = loaded->next; - free (old); - } -} - - -static void -check_rel (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) -{ - Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL); - if (data == NULL) - { - ERROR (gettext ("section [%2d] '%s': cannot get section data\n"), - idx, section_name (ebl, idx)); - return; - } - - /* Check the fields of the section header. */ - GElf_Shdr destshdr_mem; - GElf_Shdr *destshdr = NULL; - struct loaded_segment *loaded = NULL; - bool reldyn = check_reloc_shdr (ebl, ehdr, shdr, idx, ELF_T_REL, &destshdr, - &destshdr_mem, &loaded); - - Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link); - GElf_Shdr symshdr_mem; - GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem); - Elf_Data *symdata = elf_getdata (symscn, NULL); - enum load_state state = state_undecided; - - size_t sh_entsize = gelf_fsize (ebl->elf, ELF_T_REL, 1, EV_CURRENT); - for (size_t cnt = 0; cnt < shdr->sh_size / sh_entsize; ++cnt) - { - GElf_Rel rel_mem; - GElf_Rel *rel = gelf_getrel (data, cnt, &rel_mem); - if (rel == NULL) - { - ERROR (gettext ("\ -section [%2d] '%s': cannot get relocation %zu: %s\n"), - idx, section_name (ebl, idx), cnt, elf_errmsg (-1)); - continue; - } - - check_one_reloc (ebl, ehdr, shdr, idx, cnt, symshdr, symdata, - rel->r_offset, rel->r_info, destshdr, reldyn, loaded, - &state); - } - - while (loaded != NULL) - { - struct loaded_segment *old = loaded; - loaded = loaded->next; - free (old); - } -} - - -/* Number of dynamic sections. */ -static int ndynamic; - - -static void -check_dynamic (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) -{ - Elf_Data *data; - GElf_Shdr strshdr_mem; - GElf_Shdr *strshdr; - size_t cnt; - static const bool dependencies[DT_NUM][DT_NUM] = - { - [DT_NEEDED] = { [DT_STRTAB] = true }, - [DT_PLTRELSZ] = { [DT_JMPREL] = true }, - [DT_HASH] = { [DT_SYMTAB] = true }, - [DT_STRTAB] = { [DT_STRSZ] = true }, - [DT_SYMTAB] = { [DT_STRTAB] = true, [DT_SYMENT] = true }, - [DT_RELA] = { [DT_RELASZ] = true, [DT_RELAENT] = true }, - [DT_RELASZ] = { [DT_RELA] = true }, - [DT_RELAENT] = { [DT_RELA] = true }, - [DT_STRSZ] = { [DT_STRTAB] = true }, - [DT_SYMENT] = { [DT_SYMTAB] = true }, - [DT_SONAME] = { [DT_STRTAB] = true }, - [DT_RPATH] = { [DT_STRTAB] = true }, - [DT_REL] = { [DT_RELSZ] = true, [DT_RELENT] = true }, - [DT_RELSZ] = { [DT_REL] = true }, - [DT_RELENT] = { [DT_REL] = true }, - [DT_JMPREL] = { [DT_PLTRELSZ] = true, [DT_PLTREL] = true }, - [DT_RUNPATH] = { [DT_STRTAB] = true }, - [DT_PLTREL] = { [DT_JMPREL] = true }, - }; - bool has_dt[DT_NUM]; - bool has_val_dt[DT_VALNUM]; - bool has_addr_dt[DT_ADDRNUM]; - static const bool level2[DT_NUM] = - { - [DT_RPATH] = true, - [DT_SYMBOLIC] = true, - [DT_TEXTREL] = true, - [DT_BIND_NOW] = true - }; - static const bool mandatory[DT_NUM] = - { - [DT_NULL] = true, - [DT_STRTAB] = true, - [DT_SYMTAB] = true, - [DT_STRSZ] = true, - [DT_SYMENT] = true - }; - - memset (has_dt, '\0', sizeof (has_dt)); - memset (has_val_dt, '\0', sizeof (has_val_dt)); - memset (has_addr_dt, '\0', sizeof (has_addr_dt)); - - if (++ndynamic == 2) - ERROR (gettext ("more than one dynamic section present\n")); - - data = elf_getdata (elf_getscn (ebl->elf, idx), NULL); - if (data == NULL) - { - ERROR (gettext ("section [%2d] '%s': cannot get section data\n"), - idx, section_name (ebl, idx)); - return; - } - - strshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), &strshdr_mem); - if (strshdr != NULL && strshdr->sh_type != SHT_STRTAB) - ERROR (gettext ("\ -section [%2d] '%s': referenced as string table for section [%2d] '%s' but type is not SHT_STRTAB\n"), - shdr->sh_link, section_name (ebl, shdr->sh_link), - idx, section_name (ebl, idx)); - else if (strshdr == NULL) - { - ERROR (gettext ("\ -section [%2d]: referenced as string table for section [%2d] '%s' but section link value is invalid\n"), - shdr->sh_link, idx, section_name (ebl, idx)); - return; - } - - size_t sh_entsize = gelf_fsize (ebl->elf, ELF_T_DYN, 1, EV_CURRENT); - if (shdr->sh_entsize != sh_entsize) - ERROR (gettext ("\ -section [%2d] '%s': section entry size does not match ElfXX_Dyn\n"), - idx, section_name (ebl, idx)); - - if (shdr->sh_info != 0) - ERROR (gettext ("section [%2d] '%s': sh_info not zero\n"), - idx, section_name (ebl, idx)); - - bool non_null_warned = false; - for (cnt = 0; cnt < shdr->sh_size / sh_entsize; ++cnt) - { - GElf_Dyn dyn_mem; - GElf_Dyn *dyn = gelf_getdyn (data, cnt, &dyn_mem); - if (dyn == NULL) - { - ERROR (gettext ("\ -section [%2d] '%s': cannot get dynamic section entry %zu: %s\n"), - idx, section_name (ebl, idx), cnt, elf_errmsg (-1)); - continue; - } - - if (has_dt[DT_NULL] && dyn->d_tag != DT_NULL && ! non_null_warned) - { - ERROR (gettext ("\ -section [%2d] '%s': non-DT_NULL entries follow DT_NULL entry\n"), - idx, section_name (ebl, idx)); - non_null_warned = true; - } - - if (!ebl_dynamic_tag_check (ebl, dyn->d_tag)) - ERROR (gettext ("section [%2d] '%s': entry %zu: unknown tag\n"), - idx, section_name (ebl, idx), cnt); - - if (dyn->d_tag >= 0 && dyn->d_tag < DT_NUM) - { - if (has_dt[dyn->d_tag] - && dyn->d_tag != DT_NEEDED - && dyn->d_tag != DT_NULL - && dyn->d_tag != DT_POSFLAG_1) - { - char buf[50]; - ERROR (gettext ("\ -section [%2d] '%s': entry %zu: more than one entry with tag %s\n"), - idx, section_name (ebl, idx), cnt, - ebl_dynamic_tag_name (ebl, dyn->d_tag, - buf, sizeof (buf))); - } - - if (be_strict && level2[dyn->d_tag]) - { - char buf[50]; - ERROR (gettext ("\ -section [%2d] '%s': entry %zu: level 2 tag %s used\n"), - idx, section_name (ebl, idx), cnt, - ebl_dynamic_tag_name (ebl, dyn->d_tag, - buf, sizeof (buf))); - } - - has_dt[dyn->d_tag] = true; - } - else if (dyn->d_tag >= 0 && dyn->d_tag <= DT_VALRNGHI - && DT_VALTAGIDX (dyn->d_tag) < DT_VALNUM) - has_val_dt[DT_VALTAGIDX (dyn->d_tag)] = true; - else if (dyn->d_tag >= 0 && dyn->d_tag <= DT_ADDRRNGHI - && DT_ADDRTAGIDX (dyn->d_tag) < DT_ADDRNUM) - has_addr_dt[DT_ADDRTAGIDX (dyn->d_tag)] = true; - - if (dyn->d_tag == DT_PLTREL && dyn->d_un.d_val != DT_REL - && dyn->d_un.d_val != DT_RELA) - ERROR (gettext ("\ -section [%2d] '%s': entry %zu: DT_PLTREL value must be DT_REL or DT_RELA\n"), - idx, section_name (ebl, idx), cnt); - - /* Check that addresses for entries are in loaded segments. */ - switch (dyn->d_tag) - { - size_t n; - case DT_STRTAB: - /* We require the referenced section is the same as the one - specified in sh_link. */ - if (strshdr->sh_addr != dyn->d_un.d_val) - { - ERROR (gettext ("\ -section [%2d] '%s': entry %zu: pointer does not match address of section [%2d] '%s' referenced by sh_link\n"), - idx, section_name (ebl, idx), cnt, - shdr->sh_link, section_name (ebl, shdr->sh_link)); - break; - } - goto check_addr; - - default: - if (dyn->d_tag < DT_ADDRRNGLO || dyn->d_tag > DT_ADDRRNGHI) - /* Value is no pointer. */ - break; - FALLTHROUGH; - - case DT_AUXILIARY: - case DT_FILTER: - case DT_FINI: - case DT_FINI_ARRAY: - case DT_HASH: - case DT_INIT: - case DT_INIT_ARRAY: - case DT_JMPREL: - case DT_PLTGOT: - case DT_REL: - case DT_RELA: - case DT_SYMBOLIC: - case DT_SYMTAB: - case DT_VERDEF: - case DT_VERNEED: - case DT_VERSYM: - check_addr: - for (n = 0; n < phnum; ++n) - { - GElf_Phdr phdr_mem; - GElf_Phdr *phdr = gelf_getphdr (ebl->elf, n, &phdr_mem); - if (phdr != NULL && phdr->p_type == PT_LOAD - && phdr->p_vaddr <= dyn->d_un.d_ptr - && phdr->p_vaddr + phdr->p_memsz > dyn->d_un.d_ptr) - break; - } - if (unlikely (n >= phnum)) - { - char buf[50]; - ERROR (gettext ("\ -section [%2d] '%s': entry %zu: %s value must point into loaded segment\n"), - idx, section_name (ebl, idx), cnt, - ebl_dynamic_tag_name (ebl, dyn->d_tag, buf, - sizeof (buf))); - } - break; - - case DT_NEEDED: - case DT_RPATH: - case DT_RUNPATH: - case DT_SONAME: - if (dyn->d_un.d_ptr >= strshdr->sh_size) - { - char buf[50]; - ERROR (gettext ("\ -section [%2d] '%s': entry %zu: %s value must be valid offset in section [%2d] '%s'\n"), - idx, section_name (ebl, idx), cnt, - ebl_dynamic_tag_name (ebl, dyn->d_tag, buf, - sizeof (buf)), - shdr->sh_link, section_name (ebl, shdr->sh_link)); - } - break; - } - } - - for (cnt = 1; cnt < DT_NUM; ++cnt) - if (has_dt[cnt]) - { - for (int inner = 0; inner < DT_NUM; ++inner) - if (dependencies[cnt][inner] && ! has_dt[inner]) - { - char buf1[50]; - char buf2[50]; - - ERROR (gettext ("\ -section [%2d] '%s': contains %s entry but not %s\n"), - idx, section_name (ebl, idx), - ebl_dynamic_tag_name (ebl, cnt, buf1, sizeof (buf1)), - ebl_dynamic_tag_name (ebl, inner, buf2, sizeof (buf2))); - } - } - else - { - if (mandatory[cnt]) - { - char buf[50]; - ERROR (gettext ("\ -section [%2d] '%s': mandatory tag %s not present\n"), - idx, section_name (ebl, idx), - ebl_dynamic_tag_name (ebl, cnt, buf, sizeof (buf))); - } - } - - /* Make sure we have an hash table. */ - if (!has_dt[DT_HASH] && !has_addr_dt[DT_ADDRTAGIDX (DT_GNU_HASH)]) - ERROR (gettext ("\ -section [%2d] '%s': no hash section present\n"), - idx, section_name (ebl, idx)); - - /* The GNU-style hash table also needs a symbol table. */ - if (!has_dt[DT_HASH] && has_addr_dt[DT_ADDRTAGIDX (DT_GNU_HASH)] - && !has_dt[DT_SYMTAB]) - ERROR (gettext ("\ -section [%2d] '%s': contains %s entry but not %s\n"), - idx, section_name (ebl, idx), - "DT_GNU_HASH", "DT_SYMTAB"); - - /* Check the rel/rela tags. At least one group must be available. */ - if ((has_dt[DT_RELA] || has_dt[DT_RELASZ] || has_dt[DT_RELAENT]) - && (!has_dt[DT_RELA] || !has_dt[DT_RELASZ] || !has_dt[DT_RELAENT])) - ERROR (gettext ("\ -section [%2d] '%s': not all of %s, %s, and %s are present\n"), - idx, section_name (ebl, idx), - "DT_RELA", "DT_RELASZ", "DT_RELAENT"); - - if ((has_dt[DT_REL] || has_dt[DT_RELSZ] || has_dt[DT_RELENT]) - && (!has_dt[DT_REL] || !has_dt[DT_RELSZ] || !has_dt[DT_RELENT])) - ERROR (gettext ("\ -section [%2d] '%s': not all of %s, %s, and %s are present\n"), - idx, section_name (ebl, idx), - "DT_REL", "DT_RELSZ", "DT_RELENT"); - - /* Check that all prelink sections are present if any of them is. */ - if (has_val_dt[DT_VALTAGIDX (DT_GNU_PRELINKED)] - || has_val_dt[DT_VALTAGIDX (DT_CHECKSUM)]) - { - if (!has_val_dt[DT_VALTAGIDX (DT_GNU_PRELINKED)]) - ERROR (gettext ("\ -section [%2d] '%s': %s tag missing in DSO marked during prelinking\n"), - idx, section_name (ebl, idx), "DT_GNU_PRELINKED"); - if (!has_val_dt[DT_VALTAGIDX (DT_CHECKSUM)]) - ERROR (gettext ("\ -section [%2d] '%s': %s tag missing in DSO marked during prelinking\n"), - idx, section_name (ebl, idx), "DT_CHECKSUM"); - - /* Only DSOs can be marked like this. */ - if (ehdr->e_type != ET_DYN) - ERROR (gettext ("\ -section [%2d] '%s': non-DSO file marked as dependency during prelink\n"), - idx, section_name (ebl, idx)); - } - - if (has_val_dt[DT_VALTAGIDX (DT_GNU_CONFLICTSZ)] - || has_val_dt[DT_VALTAGIDX (DT_GNU_LIBLISTSZ)] - || has_addr_dt[DT_ADDRTAGIDX (DT_GNU_CONFLICT)] - || has_addr_dt[DT_ADDRTAGIDX (DT_GNU_LIBLIST)]) - { - if (!has_val_dt[DT_VALTAGIDX (DT_GNU_CONFLICTSZ)]) - ERROR (gettext ("\ -section [%2d] '%s': %s tag missing in prelinked executable\n"), - idx, section_name (ebl, idx), "DT_GNU_CONFLICTSZ"); - if (!has_val_dt[DT_VALTAGIDX (DT_GNU_LIBLISTSZ)]) - ERROR (gettext ("\ -section [%2d] '%s': %s tag missing in prelinked executable\n"), - idx, section_name (ebl, idx), "DT_GNU_LIBLISTSZ"); - if (!has_addr_dt[DT_ADDRTAGIDX (DT_GNU_CONFLICT)]) - ERROR (gettext ("\ -section [%2d] '%s': %s tag missing in prelinked executable\n"), - idx, section_name (ebl, idx), "DT_GNU_CONFLICT"); - if (!has_addr_dt[DT_ADDRTAGIDX (DT_GNU_LIBLIST)]) - ERROR (gettext ("\ -section [%2d] '%s': %s tag missing in prelinked executable\n"), - idx, section_name (ebl, idx), "DT_GNU_LIBLIST"); - } -} - - -static void -check_symtab_shndx (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) -{ - if (ehdr->e_type != ET_REL) - { - ERROR (gettext ("\ -section [%2d] '%s': only relocatable files can have extended section index\n"), - idx, section_name (ebl, idx)); - return; - } - - Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link); - GElf_Shdr symshdr_mem; - GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem); - if (symshdr != NULL && symshdr->sh_type != SHT_SYMTAB) - ERROR (gettext ("\ -section [%2d] '%s': extended section index section not for symbol table\n"), - idx, section_name (ebl, idx)); - else if (symshdr == NULL) - ERROR (gettext ("\ -section [%2d] '%s': sh_link extended section index [%2d] is invalid\n"), - idx, section_name (ebl, idx), shdr->sh_link); - Elf_Data *symdata = elf_getdata (symscn, NULL); - if (symdata == NULL) - ERROR (gettext ("cannot get data for symbol section\n")); - - if (shdr->sh_entsize != sizeof (Elf32_Word)) - ERROR (gettext ("\ -section [%2d] '%s': entry size does not match Elf32_Word\n"), - idx, section_name (ebl, idx)); - - if (symshdr != NULL - && shdr->sh_entsize != 0 - && symshdr->sh_entsize != 0 - && (shdr->sh_size / shdr->sh_entsize - < symshdr->sh_size / symshdr->sh_entsize)) - ERROR (gettext ("\ -section [%2d] '%s': extended index table too small for symbol table\n"), - idx, section_name (ebl, idx)); - - if (shdr->sh_info != 0) - ERROR (gettext ("section [%2d] '%s': sh_info not zero\n"), - idx, section_name (ebl, idx)); - - for (size_t cnt = idx + 1; cnt < shnum; ++cnt) - { - GElf_Shdr rshdr_mem; - GElf_Shdr *rshdr = gelf_getshdr (elf_getscn (ebl->elf, cnt), &rshdr_mem); - if (rshdr != NULL && rshdr->sh_type == SHT_SYMTAB_SHNDX - && rshdr->sh_link == shdr->sh_link) - { - ERROR (gettext ("\ -section [%2d] '%s': extended section index in section [%2zu] '%s' refers to same symbol table\n"), - idx, section_name (ebl, idx), - cnt, section_name (ebl, cnt)); - break; - } - } - - Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL); - if (data == NULL || data->d_buf == NULL) - { - ERROR (gettext ("section [%2d] '%s': cannot get section data\n"), - idx, section_name (ebl, idx)); - return; - } - - if (data->d_size < sizeof (Elf32_Word) - || *((Elf32_Word *) data->d_buf) != 0) - ERROR (gettext ("symbol 0 should have zero extended section index\n")); - - for (size_t cnt = 1; cnt < data->d_size / sizeof (Elf32_Word); ++cnt) - { - Elf32_Word xndx = ((Elf32_Word *) data->d_buf)[cnt]; - - if (xndx != 0) - { - GElf_Sym sym_data; - GElf_Sym *sym = gelf_getsym (symdata, cnt, &sym_data); - if (sym == NULL) - { - ERROR (gettext ("cannot get data for symbol %zu\n"), cnt); - continue; - } - - if (sym->st_shndx != SHN_XINDEX) - ERROR (gettext ("\ -extended section index is %" PRIu32 " but symbol index is not XINDEX\n"), - (uint32_t) xndx); - } - } -} - - -static void -check_sysv_hash (Ebl *ebl, GElf_Shdr *shdr, Elf_Data *data, int idx, - GElf_Shdr *symshdr) -{ - Elf32_Word nbucket = ((Elf32_Word *) data->d_buf)[0]; - Elf32_Word nchain = ((Elf32_Word *) data->d_buf)[1]; - - if (shdr->sh_size < (2ULL + nbucket + nchain) * sizeof (Elf32_Word)) - { - ERROR (gettext ("\ -section [%2d] '%s': hash table section is too small (is %ld, expected %ld)\n"), - idx, section_name (ebl, idx), (long int) shdr->sh_size, - (long int) ((2 + nbucket + nchain) * sizeof (Elf32_Word))); - return; - } - - size_t maxidx = nchain; - - if (symshdr != NULL && symshdr->sh_entsize != 0) - { - size_t symsize = symshdr->sh_size / symshdr->sh_entsize; - - if (nchain > symshdr->sh_size / symshdr->sh_entsize) - ERROR (gettext ("section [%2d] '%s': chain array too large\n"), - idx, section_name (ebl, idx)); - - maxidx = symsize; - } - - Elf32_Word *buf = (Elf32_Word *) data->d_buf; - Elf32_Word *end = (Elf32_Word *) ((char *) data->d_buf + shdr->sh_size); - size_t cnt; - for (cnt = 2; cnt < 2 + nbucket; ++cnt) - { - if (buf + cnt >= end) - break; - else if (buf[cnt] >= maxidx) - ERROR (gettext ("\ -section [%2d] '%s': hash bucket reference %zu out of bounds\n"), - idx, section_name (ebl, idx), cnt - 2); - } - - for (; cnt < 2 + nbucket + nchain; ++cnt) - { - if (buf + cnt >= end) - break; - else if (buf[cnt] >= maxidx) - ERROR (gettext ("\ -section [%2d] '%s': hash chain reference %zu out of bounds\n"), - idx, section_name (ebl, idx), cnt - 2 - nbucket); - } -} - - -static void -check_sysv_hash64 (Ebl *ebl, GElf_Shdr *shdr, Elf_Data *data, int idx, - GElf_Shdr *symshdr) -{ - Elf64_Xword nbucket = ((Elf64_Xword *) data->d_buf)[0]; - Elf64_Xword nchain = ((Elf64_Xword *) data->d_buf)[1]; - - uint64_t maxwords = shdr->sh_size / sizeof (Elf64_Xword); - if (maxwords < 2 - || maxwords - 2 < nbucket - || maxwords - 2 - nbucket < nchain) - { - ERROR (gettext ("\ -section [%2d] '%s': hash table section is too small (is %ld, expected %ld)\n"), - idx, section_name (ebl, idx), (long int) shdr->sh_size, - (long int) ((2 + nbucket + nchain) * sizeof (Elf64_Xword))); - return; - } - - size_t maxidx = nchain; - - if (symshdr != NULL && symshdr->sh_entsize != 0) - { - size_t symsize = symshdr->sh_size / symshdr->sh_entsize; - - if (nchain > symshdr->sh_size / symshdr->sh_entsize) - ERROR (gettext ("section [%2d] '%s': chain array too large\n"), - idx, section_name (ebl, idx)); - - maxidx = symsize; - } - - Elf64_Xword *buf = (Elf64_Xword *) data->d_buf; - Elf64_Xword *end = (Elf64_Xword *) ((char *) data->d_buf + shdr->sh_size); - size_t cnt; - for (cnt = 2; cnt < 2 + nbucket; ++cnt) - { - if (buf + cnt >= end) - break; - else if (buf[cnt] >= maxidx) - ERROR (gettext ("\ -section [%2d] '%s': hash bucket reference %zu out of bounds\n"), - idx, section_name (ebl, idx), cnt - 2); - } - - for (; cnt < 2 + nbucket + nchain; ++cnt) - { - if (buf + cnt >= end) - break; - else if (buf[cnt] >= maxidx) - ERROR (gettext ("\ -section [%2d] '%s': hash chain reference %" PRIu64 " out of bounds\n"), - idx, section_name (ebl, idx), (uint64_t) cnt - 2 - nbucket); - } -} - - -static void -check_gnu_hash (Ebl *ebl, GElf_Shdr *shdr, Elf_Data *data, int idx, - GElf_Shdr *symshdr) -{ - if (data->d_size < 4 * sizeof (Elf32_Word)) - { - ERROR (gettext ("\ -section [%2d] '%s': not enough data\n"), - idx, section_name (ebl, idx)); - return; - } - - Elf32_Word nbuckets = ((Elf32_Word *) data->d_buf)[0]; - Elf32_Word symbias = ((Elf32_Word *) data->d_buf)[1]; - Elf32_Word bitmask_words = ((Elf32_Word *) data->d_buf)[2]; - - if (bitmask_words == 0 || !powerof2 (bitmask_words)) - { - ERROR (gettext ("\ -section [%2d] '%s': bitmask size zero or not power of 2: %u\n"), - idx, section_name (ebl, idx), bitmask_words); - return; - } - - size_t bitmask_idxmask = bitmask_words - 1; - if (gelf_getclass (ebl->elf) == ELFCLASS64) - bitmask_words *= 2; - Elf32_Word shift = ((Elf32_Word *) data->d_buf)[3]; - - /* Is there still room for the sym chain? - Use uint64_t calculation to prevent 32bit overlow. */ - uint64_t used_buf = (4ULL + bitmask_words + nbuckets) * sizeof (Elf32_Word); - if (used_buf > data->d_size) - { - ERROR (gettext ("\ -section [%2d] '%s': hash table section is too small (is %ld, expected at least %ld)\n"), - idx, section_name (ebl, idx), (long int) shdr->sh_size, - (long int) used_buf); - return; - } - - if (shift > 31) - { - ERROR (gettext ("\ -section [%2d] '%s': 2nd hash function shift too big: %u\n"), - idx, section_name (ebl, idx), shift); - return; - } - - size_t maxidx = shdr->sh_size / sizeof (Elf32_Word) - (4 + bitmask_words - + nbuckets); - - if (symshdr != NULL && symshdr->sh_entsize != 0) - maxidx = MIN (maxidx, symshdr->sh_size / symshdr->sh_entsize); - - /* We need the symbol section data. */ - Elf_Data *symdata = elf_getdata (elf_getscn (ebl->elf, shdr->sh_link), NULL); - - union - { - Elf32_Word *p32; - Elf64_Xword *p64; - } bitmask = { .p32 = &((Elf32_Word *) data->d_buf)[4] }, - collected = { .p32 = xcalloc (bitmask_words, sizeof (Elf32_Word)) }; - - size_t classbits = gelf_getclass (ebl->elf) == ELFCLASS32 ? 32 : 64; - - size_t cnt; - for (cnt = 4 + bitmask_words; cnt < 4 + bitmask_words + nbuckets; ++cnt) - { - Elf32_Word symidx = ((Elf32_Word *) data->d_buf)[cnt]; - - if (symidx == 0) - continue; - - if (symidx < symbias) - { - ERROR (gettext ("\ -section [%2d] '%s': hash chain for bucket %zu lower than symbol index bias\n"), - idx, section_name (ebl, idx), cnt - (4 + bitmask_words)); - continue; - } - - while (symidx - symbias < maxidx) - { - Elf32_Word chainhash = ((Elf32_Word *) data->d_buf)[4 - + bitmask_words - + nbuckets - + symidx - - symbias]; - - if (symdata != NULL) - { - /* Check that the referenced symbol is not undefined. */ - GElf_Sym sym_mem; - GElf_Sym *sym = gelf_getsym (symdata, symidx, &sym_mem); - if (sym != NULL && sym->st_shndx == SHN_UNDEF - && GELF_ST_TYPE (sym->st_info) != STT_FUNC) - ERROR (gettext ("\ -section [%2d] '%s': symbol %u referenced in chain for bucket %zu is undefined\n"), - idx, section_name (ebl, idx), symidx, - cnt - (4 + bitmask_words)); - - const char *symname = (sym != NULL - ? elf_strptr (ebl->elf, symshdr->sh_link, - sym->st_name) - : NULL); - if (symname != NULL) - { - Elf32_Word hval = elf_gnu_hash (symname); - if ((hval & ~1u) != (chainhash & ~1u)) - ERROR (gettext ("\ -section [%2d] '%s': hash value for symbol %u in chain for bucket %zu wrong\n"), - idx, section_name (ebl, idx), symidx, - cnt - (4 + bitmask_words)); - - /* Set the bits in the bitmask. */ - size_t maskidx = (hval / classbits) & bitmask_idxmask; - if (maskidx >= bitmask_words) - { - ERROR (gettext ("\ -section [%2d] '%s': mask index for symbol %u in chain for bucket %zu wrong\n"), - idx, section_name (ebl, idx), symidx, - cnt - (4 + bitmask_words)); - return; - } - if (classbits == 32) - { - collected.p32[maskidx] - |= UINT32_C (1) << (hval & (classbits - 1)); - collected.p32[maskidx] - |= UINT32_C (1) << ((hval >> shift) & (classbits - 1)); - } - else - { - collected.p64[maskidx] - |= UINT64_C (1) << (hval & (classbits - 1)); - collected.p64[maskidx] - |= UINT64_C (1) << ((hval >> shift) & (classbits - 1)); - } - } - } - - if ((chainhash & 1) != 0) - break; - - ++symidx; - } - - if (symidx - symbias >= maxidx) - ERROR (gettext ("\ -section [%2d] '%s': hash chain for bucket %zu out of bounds\n"), - idx, section_name (ebl, idx), cnt - (4 + bitmask_words)); - else if (symshdr != NULL && symshdr->sh_entsize != 0 - && symidx > symshdr->sh_size / symshdr->sh_entsize) - ERROR (gettext ("\ -section [%2d] '%s': symbol reference in chain for bucket %zu out of bounds\n"), - idx, section_name (ebl, idx), cnt - (4 + bitmask_words)); - } - - if (memcmp (collected.p32, bitmask.p32, bitmask_words * sizeof (Elf32_Word))) - ERROR (gettext ("\ -section [%2d] '%s': bitmask does not match names in the hash table\n"), - idx, section_name (ebl, idx)); - - free (collected.p32); -} - - -static void -check_hash (int tag, Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) -{ - if (ehdr->e_type == ET_REL) - { - ERROR (gettext ("\ -section [%2d] '%s': relocatable files cannot have hash tables\n"), - idx, section_name (ebl, idx)); - return; - } - - Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL); - if (data == NULL || data->d_buf == NULL) - { - ERROR (gettext ("section [%2d] '%s': cannot get section data\n"), - idx, section_name (ebl, idx)); - return; - } - - GElf_Shdr symshdr_mem; - GElf_Shdr *symshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), - &symshdr_mem); - if (symshdr != NULL && symshdr->sh_type != SHT_DYNSYM) - ERROR (gettext ("\ -section [%2d] '%s': hash table not for dynamic symbol table\n"), - idx, section_name (ebl, idx)); - else if (symshdr == NULL) - ERROR (gettext ("\ -section [%2d] '%s': invalid sh_link symbol table section index [%2d]\n"), - idx, section_name (ebl, idx), shdr->sh_link); - - size_t expect_entsize = (tag == SHT_GNU_HASH - ? (gelf_getclass (ebl->elf) == ELFCLASS32 - ? sizeof (Elf32_Word) : 0) - : (size_t) ebl_sysvhash_entrysize (ebl)); - - if (shdr->sh_entsize != expect_entsize) - ERROR (gettext ("\ -section [%2d] '%s': hash table entry size incorrect\n"), - idx, section_name (ebl, idx)); - - if ((shdr->sh_flags & SHF_ALLOC) == 0) - ERROR (gettext ("section [%2d] '%s': not marked to be allocated\n"), - idx, section_name (ebl, idx)); - - if (shdr->sh_size < (tag == SHT_GNU_HASH ? 4 : 2) * (expect_entsize ?: 4)) - { - ERROR (gettext ("\ -section [%2d] '%s': hash table has not even room for initial administrative entries\n"), - idx, section_name (ebl, idx)); - return; - } - - switch (tag) - { - case SHT_HASH: - if (ebl_sysvhash_entrysize (ebl) == sizeof (Elf64_Xword)) - check_sysv_hash64 (ebl, shdr, data, idx, symshdr); - else - check_sysv_hash (ebl, shdr, data, idx, symshdr); - break; - - case SHT_GNU_HASH: - check_gnu_hash (ebl, shdr, data, idx, symshdr); - break; - - default: - assert (! "should not happen"); - } -} - - -/* Compare content of both hash tables, it must be identical. */ -static void -compare_hash_gnu_hash (Ebl *ebl, GElf_Ehdr *ehdr, size_t hash_idx, - size_t gnu_hash_idx) -{ - Elf_Scn *hash_scn = elf_getscn (ebl->elf, hash_idx); - Elf_Data *hash_data = elf_getdata (hash_scn, NULL); - GElf_Shdr hash_shdr_mem; - GElf_Shdr *hash_shdr = gelf_getshdr (hash_scn, &hash_shdr_mem); - Elf_Scn *gnu_hash_scn = elf_getscn (ebl->elf, gnu_hash_idx); - Elf_Data *gnu_hash_data = elf_getdata (gnu_hash_scn, NULL); - GElf_Shdr gnu_hash_shdr_mem; - GElf_Shdr *gnu_hash_shdr = gelf_getshdr (gnu_hash_scn, &gnu_hash_shdr_mem); - - if (hash_shdr == NULL || gnu_hash_shdr == NULL - || hash_data == NULL || hash_data->d_buf == NULL - || gnu_hash_data == NULL || gnu_hash_data->d_buf == NULL) - /* None of these pointers should be NULL since we used the - sections already. We are careful nonetheless. */ - return; - - /* The link must point to the same symbol table. */ - if (hash_shdr->sh_link != gnu_hash_shdr->sh_link) - { - ERROR (gettext ("\ -sh_link in hash sections [%2zu] '%s' and [%2zu] '%s' not identical\n"), - hash_idx, elf_strptr (ebl->elf, shstrndx, hash_shdr->sh_name), - gnu_hash_idx, - elf_strptr (ebl->elf, shstrndx, gnu_hash_shdr->sh_name)); - return; - } - - Elf_Scn *sym_scn = elf_getscn (ebl->elf, hash_shdr->sh_link); - Elf_Data *sym_data = elf_getdata (sym_scn, NULL); - GElf_Shdr sym_shdr_mem; - GElf_Shdr *sym_shdr = gelf_getshdr (sym_scn, &sym_shdr_mem); - - if (sym_data == NULL || sym_data->d_buf == NULL - || sym_shdr == NULL || sym_shdr->sh_entsize == 0) - return; - - const char *hash_name; - const char *gnu_hash_name; - hash_name = elf_strptr (ebl->elf, shstrndx, hash_shdr->sh_name); - gnu_hash_name = elf_strptr (ebl->elf, shstrndx, gnu_hash_shdr->sh_name); - - if (gnu_hash_data->d_size < 4 * sizeof (Elf32_Word)) - { - ERROR (gettext ("\ -hash section [%2zu] '%s' does not contain enough data\n"), - gnu_hash_idx, gnu_hash_name); - return; - } - - uint32_t nentries = sym_shdr->sh_size / sym_shdr->sh_entsize; - char *used = alloca (nentries); - memset (used, '\0', nentries); - - /* First go over the GNU_HASH table and mark the entries as used. */ - const Elf32_Word *gnu_hasharr = (Elf32_Word *) gnu_hash_data->d_buf; - Elf32_Word gnu_nbucket = gnu_hasharr[0]; - Elf32_Word gnu_symbias = gnu_hasharr[1]; - const int bitmap_factor = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 1 : 2; - const Elf32_Word *gnu_bucket = (gnu_hasharr - + (4 + gnu_hasharr[2] * bitmap_factor)); - const Elf32_Word *gnu_chain = gnu_bucket + gnu_hasharr[0]; - - if (gnu_hasharr[2] == 0) - { - ERROR (gettext ("\ -hash section [%2zu] '%s' has zero bit mask words\n"), - gnu_hash_idx, gnu_hash_name); - return; - } - - uint64_t used_buf = ((4ULL + gnu_hasharr[2] * bitmap_factor + gnu_nbucket) - * sizeof (Elf32_Word)); - uint32_t max_nsyms = (gnu_hash_data->d_size - used_buf) / sizeof (Elf32_Word); - if (used_buf > gnu_hash_data->d_size) - { - ERROR (gettext ("\ -hash section [%2zu] '%s' uses too much data\n"), - gnu_hash_idx, gnu_hash_name); - return; - } - - for (Elf32_Word cnt = 0; cnt < gnu_nbucket; ++cnt) - { - if (gnu_bucket[cnt] != STN_UNDEF) - { - Elf32_Word symidx = gnu_bucket[cnt] - gnu_symbias; - do - { - if (symidx >= max_nsyms || symidx + gnu_symbias >= nentries) - { - ERROR (gettext ("\ -hash section [%2zu] '%s' invalid symbol index %" PRIu32 " (max_nsyms: %" PRIu32 ", nentries: %" PRIu32 "\n"), - gnu_hash_idx, gnu_hash_name, symidx, max_nsyms, nentries); - return; - } - used[symidx + gnu_symbias] |= 1; - } - while ((gnu_chain[symidx++] & 1u) == 0); - } - } - - /* Now go over the old hash table and check that we cover the same - entries. */ - if (hash_shdr->sh_entsize == sizeof (Elf32_Word)) - { - const Elf32_Word *hasharr = (Elf32_Word *) hash_data->d_buf; - if (hash_data->d_size < 2 * sizeof (Elf32_Word)) - { - ERROR (gettext ("\ -hash section [%2zu] '%s' does not contain enough data\n"), - hash_idx, hash_name); - return; - } - - Elf32_Word nbucket = hasharr[0]; - Elf32_Word nchain = hasharr[1]; - uint64_t hash_used = (2ULL + nchain + nbucket) * sizeof (Elf32_Word); - if (hash_used > hash_data->d_size) - { - ERROR (gettext ("\ -hash section [%2zu] '%s' uses too much data\n"), - hash_idx, hash_name); - return; - } - - const Elf32_Word *bucket = &hasharr[2]; - const Elf32_Word *chain = &hasharr[2 + nbucket]; - - for (Elf32_Word cnt = 0; cnt < nbucket; ++cnt) - { - Elf32_Word symidx = bucket[cnt]; - while (symidx != STN_UNDEF && symidx < nentries && symidx < nchain) - { - used[symidx] |= 2; - symidx = chain[symidx]; - } - } - } - else if (hash_shdr->sh_entsize == sizeof (Elf64_Xword)) - { - const Elf64_Xword *hasharr = (Elf64_Xword *) hash_data->d_buf; - if (hash_data->d_size < 2 * sizeof (Elf32_Word)) - { - ERROR (gettext ("\ -hash section [%2zu] '%s' does not contain enough data\n"), - hash_idx, hash_name); - return; - } - - Elf64_Xword nbucket = hasharr[0]; - Elf64_Xword nchain = hasharr[1]; - uint64_t maxwords = hash_data->d_size / sizeof (Elf64_Xword); - if (maxwords < 2 - || maxwords - 2 < nbucket - || maxwords - 2 - nbucket < nchain) - { - ERROR (gettext ("\ -hash section [%2zu] '%s' uses too much data\n"), - hash_idx, hash_name); - return; - } - - const Elf64_Xword *bucket = &hasharr[2]; - const Elf64_Xword *chain = &hasharr[2 + nbucket]; - - for (Elf64_Xword cnt = 0; cnt < nbucket; ++cnt) - { - Elf64_Xword symidx = bucket[cnt]; - while (symidx != STN_UNDEF && symidx < nentries && symidx < nchain) - { - used[symidx] |= 2; - symidx = chain[symidx]; - } - } - } - else - { - ERROR (gettext ("\ -hash section [%2zu] '%s' invalid sh_entsize\n"), - hash_idx, hash_name); - return; - } - - /* Now see which entries are not set in one or both hash tables - (unless the symbol is undefined in which case it can be omitted - in the new table format). */ - if ((used[0] & 1) != 0) - ERROR (gettext ("section [%2zu] '%s': reference to symbol index 0\n"), - gnu_hash_idx, - elf_strptr (ebl->elf, shstrndx, gnu_hash_shdr->sh_name)); - if ((used[0] & 2) != 0) - ERROR (gettext ("section [%2zu] '%s': reference to symbol index 0\n"), - hash_idx, elf_strptr (ebl->elf, shstrndx, hash_shdr->sh_name)); - - for (uint32_t cnt = 1; cnt < nentries; ++cnt) - if (used[cnt] != 0 && used[cnt] != 3) - { - if (used[cnt] == 1) - ERROR (gettext ("\ -symbol %d referenced in new hash table in [%2zu] '%s' but not in old hash table in [%2zu] '%s'\n"), - cnt, gnu_hash_idx, - elf_strptr (ebl->elf, shstrndx, gnu_hash_shdr->sh_name), - hash_idx, - elf_strptr (ebl->elf, shstrndx, hash_shdr->sh_name)); - else - { - GElf_Sym sym_mem; - GElf_Sym *sym = gelf_getsym (sym_data, cnt, &sym_mem); - - if (sym != NULL && sym->st_shndx != STN_UNDEF) - ERROR (gettext ("\ -symbol %d referenced in old hash table in [%2zu] '%s' but not in new hash table in [%2zu] '%s'\n"), - cnt, hash_idx, - elf_strptr (ebl->elf, shstrndx, hash_shdr->sh_name), - gnu_hash_idx, - elf_strptr (ebl->elf, shstrndx, gnu_hash_shdr->sh_name)); - } - } -} - - -static void -check_null (Ebl *ebl, GElf_Shdr *shdr, int idx) -{ -#define TEST(name, extra) \ - if (extra && shdr->sh_##name != 0) \ - ERROR (gettext ("section [%2d] '%s': nonzero sh_%s for NULL section\n"), \ - idx, section_name (ebl, idx), #name) - - TEST (name, 1); - TEST (flags, 1); - TEST (addr, 1); - TEST (offset, 1); - TEST (size, idx != 0); - TEST (link, idx != 0); - TEST (info, 1); - TEST (addralign, 1); - TEST (entsize, 1); -} - - -static void -check_group (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) -{ - if (ehdr->e_type != ET_REL) - { - ERROR (gettext ("\ -section [%2d] '%s': section groups only allowed in relocatable object files\n"), - idx, section_name (ebl, idx)); - return; - } - - /* Check that sh_link is an index of a symbol table. */ - Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link); - GElf_Shdr symshdr_mem; - GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem); - if (symshdr == NULL) - ERROR (gettext ("section [%2d] '%s': cannot get symbol table: %s\n"), - idx, section_name (ebl, idx), elf_errmsg (-1)); - else - { - if (symshdr->sh_type != SHT_SYMTAB) - ERROR (gettext ("\ -section [%2d] '%s': section reference in sh_link is no symbol table\n"), - idx, section_name (ebl, idx)); - - if (shdr->sh_info >= symshdr->sh_size / gelf_fsize (ebl->elf, ELF_T_SYM, - 1, EV_CURRENT)) - ERROR (gettext ("\ -section [%2d] '%s': invalid symbol index in sh_info\n"), - idx, section_name (ebl, idx)); - - if (shdr->sh_flags != 0) - ERROR (gettext ("section [%2d] '%s': sh_flags not zero\n"), - idx, section_name (ebl, idx)); - - GElf_Sym sym_data; - GElf_Sym *sym = gelf_getsym (elf_getdata (symscn, NULL), shdr->sh_info, - &sym_data); - if (sym == NULL) - ERROR (gettext ("\ -section [%2d] '%s': cannot get symbol for signature\n"), - idx, section_name (ebl, idx)); - else if (elf_strptr (ebl->elf, symshdr->sh_link, sym->st_name) == NULL) - ERROR (gettext ("\ -section [%2d] '%s': cannot get symbol name for signature\n"), - idx, section_name (ebl, idx)); - else if (strcmp (elf_strptr (ebl->elf, symshdr->sh_link, sym->st_name), - "") == 0) - ERROR (gettext ("\ -section [%2d] '%s': signature symbol cannot be empty string\n"), - idx, section_name (ebl, idx)); - - if (be_strict - && shdr->sh_entsize != elf32_fsize (ELF_T_WORD, 1, EV_CURRENT)) - ERROR (gettext ("section [%2d] '%s': sh_flags not set correctly\n"), - idx, section_name (ebl, idx)); - } - - Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL); - if (data == NULL || data->d_buf == NULL) - ERROR (gettext ("section [%2d] '%s': cannot get data: %s\n"), - idx, section_name (ebl, idx), elf_errmsg (-1)); - else - { - size_t elsize = elf32_fsize (ELF_T_WORD, 1, EV_CURRENT); - size_t cnt; - Elf32_Word val; - - if (data->d_size % elsize != 0) - ERROR (gettext ("\ -section [%2d] '%s': section size not multiple of sizeof(Elf32_Word)\n"), - idx, section_name (ebl, idx)); - - if (data->d_size < elsize) - { - ERROR (gettext ("\ -section [%2d] '%s': section group without flags word\n"), - idx, section_name (ebl, idx)); - return; - } - else if (be_strict) - { - if (data->d_size < 2 * elsize) - ERROR (gettext ("\ -section [%2d] '%s': section group without member\n"), - idx, section_name (ebl, idx)); - else if (data->d_size < 3 * elsize) - ERROR (gettext ("\ -section [%2d] '%s': section group with only one member\n"), - idx, section_name (ebl, idx)); - } - -#if ALLOW_UNALIGNED - val = *((Elf32_Word *) data->d_buf); -#else - memcpy (&val, data->d_buf, elsize); -#endif - if ((val & ~GRP_COMDAT) != 0) - ERROR (gettext ("section [%2d] '%s': unknown section group flags\n"), - idx, section_name (ebl, idx)); - - for (cnt = elsize; cnt + elsize <= data->d_size; cnt += elsize) - { -#if ALLOW_UNALIGNED - val = *((Elf32_Word *) ((char *) data->d_buf + cnt)); -#else - memcpy (&val, (char *) data->d_buf + cnt, elsize); -#endif - - if (val > shnum) - ERROR (gettext ("\ -section [%2d] '%s': section index %zu out of range\n"), - idx, section_name (ebl, idx), cnt / elsize); - else - { - GElf_Shdr refshdr_mem; - GElf_Shdr *refshdr = gelf_getshdr (elf_getscn (ebl->elf, val), - &refshdr_mem); - if (refshdr == NULL) - ERROR (gettext ("\ -section [%2d] '%s': cannot get section header for element %zu: %s\n"), - idx, section_name (ebl, idx), cnt / elsize, - elf_errmsg (-1)); - else - { - if (refshdr->sh_type == SHT_GROUP) - ERROR (gettext ("\ -section [%2d] '%s': section group contains another group [%2d] '%s'\n"), - idx, section_name (ebl, idx), - val, section_name (ebl, val)); - - if ((refshdr->sh_flags & SHF_GROUP) == 0) - ERROR (gettext ("\ -section [%2d] '%s': element %zu references section [%2d] '%s' without SHF_GROUP flag set\n"), - idx, section_name (ebl, idx), cnt / elsize, - val, section_name (ebl, val)); - } - - if (val < shnum && ++scnref[val] == 2) - ERROR (gettext ("\ -section [%2d] '%s' is contained in more than one section group\n"), - val, section_name (ebl, val)); - } - } - } -} - - -static const char * -section_flags_string (GElf_Word flags, char *buf, size_t len) -{ - if (flags == 0) - return "none"; - - static const struct - { - GElf_Word flag; - const char *name; - } known_flags[] = - { -#define NEWFLAG(name) { SHF_##name, #name } - NEWFLAG (WRITE), - NEWFLAG (ALLOC), - NEWFLAG (EXECINSTR), - NEWFLAG (MERGE), - NEWFLAG (STRINGS), - NEWFLAG (INFO_LINK), - NEWFLAG (LINK_ORDER), - NEWFLAG (OS_NONCONFORMING), - NEWFLAG (GROUP), - NEWFLAG (TLS), - NEWFLAG (COMPRESSED) - }; -#undef NEWFLAG - const size_t nknown_flags = sizeof (known_flags) / sizeof (known_flags[0]); - - char *cp = buf; - - for (size_t cnt = 0; cnt < nknown_flags; ++cnt) - if (flags & known_flags[cnt].flag) - { - if (cp != buf && len > 1) - { - *cp++ = '|'; - --len; - } - - size_t ncopy = MIN (len - 1, strlen (known_flags[cnt].name)); - cp = mempcpy (cp, known_flags[cnt].name, ncopy); - len -= ncopy; - - flags ^= known_flags[cnt].flag; - } - - if (flags != 0 || cp == buf) - snprintf (cp, len - 1, "%" PRIx64, (uint64_t) flags); - - *cp = '\0'; - - return buf; -} - - -static int -has_copy_reloc (Ebl *ebl, unsigned int symscnndx, unsigned int symndx) -{ - /* First find the relocation section for the symbol table. */ - Elf_Scn *scn = NULL; - GElf_Shdr shdr_mem; - GElf_Shdr *shdr = NULL; - while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) - { - shdr = gelf_getshdr (scn, &shdr_mem); - if (shdr != NULL - && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA) - && shdr->sh_link == symscnndx) - /* Found the section. */ - break; - } - - if (scn == NULL) - return 0; - - Elf_Data *data = elf_getdata (scn, NULL); - if (data == NULL || shdr->sh_entsize == 0) - return 0; - - if (shdr->sh_type == SHT_REL) - for (int i = 0; (size_t) i < shdr->sh_size / shdr->sh_entsize; ++i) - { - GElf_Rel rel_mem; - GElf_Rel *rel = gelf_getrel (data, i, &rel_mem); - if (rel == NULL) - continue; - - if (GELF_R_SYM (rel->r_info) == symndx - && ebl_copy_reloc_p (ebl, GELF_R_TYPE (rel->r_info))) - return 1; - } - else - for (int i = 0; (size_t) i < shdr->sh_size / shdr->sh_entsize; ++i) - { - GElf_Rela rela_mem; - GElf_Rela *rela = gelf_getrela (data, i, &rela_mem); - if (rela == NULL) - continue; - - if (GELF_R_SYM (rela->r_info) == symndx - && ebl_copy_reloc_p (ebl, GELF_R_TYPE (rela->r_info))) - return 1; - } - - return 0; -} - - -static int -in_nobits_scn (Ebl *ebl, unsigned int shndx) -{ - GElf_Shdr shdr_mem; - GElf_Shdr *shdr = gelf_getshdr (elf_getscn (ebl->elf, shndx), &shdr_mem); - return shdr != NULL && shdr->sh_type == SHT_NOBITS; -} - - -static struct version_namelist -{ - const char *objname; - const char *name; - GElf_Versym ndx; - enum { ver_def, ver_need } type; - struct version_namelist *next; -} *version_namelist; - - -static int -add_version (const char *objname, const char *name, GElf_Versym ndx, int type) -{ - /* Check that there are no duplications. */ - struct version_namelist *nlp = version_namelist; - while (nlp != NULL) - { - if (((nlp->objname == NULL && objname == NULL) - || (nlp->objname != NULL && objname != NULL - && strcmp (nlp->objname, objname) == 0)) - && strcmp (nlp->name, name) == 0) - return nlp->type == ver_def ? 1 : -1; - nlp = nlp->next; - } - - nlp = xmalloc (sizeof (*nlp)); - nlp->objname = objname; - nlp->name = name; - nlp->ndx = ndx; - nlp->type = type; - nlp->next = version_namelist; - version_namelist = nlp; - - return 0; -} - - -static void -check_versym (Ebl *ebl, int idx) -{ - Elf_Scn *scn = elf_getscn (ebl->elf, idx); - GElf_Shdr shdr_mem; - GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); - if (shdr == NULL) - /* The error has already been reported. */ - return; - - Elf_Data *data = elf_getdata (scn, NULL); - if (data == NULL) - { - ERROR (gettext ("section [%2d] '%s': cannot get section data\n"), - idx, section_name (ebl, idx)); - return; - } - - Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link); - GElf_Shdr symshdr_mem; - GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem); - if (symshdr == NULL) - /* The error has already been reported. */ - return; - - if (symshdr->sh_type != SHT_DYNSYM) - { - ERROR (gettext ("\ -section [%2d] '%s' refers in sh_link to section [%2d] '%s' which is no dynamic symbol table\n"), - idx, section_name (ebl, idx), - shdr->sh_link, section_name (ebl, shdr->sh_link)); - return; - } - - /* The number of elements in the version symbol table must be the - same as the number of symbols. */ - if (shdr->sh_entsize != 0 && symshdr->sh_entsize != 0 - && (shdr->sh_size / shdr->sh_entsize - != symshdr->sh_size / symshdr->sh_entsize)) - ERROR (gettext ("\ -section [%2d] '%s' has different number of entries than symbol table [%2d] '%s'\n"), - idx, section_name (ebl, idx), - shdr->sh_link, section_name (ebl, shdr->sh_link)); - - Elf_Data *symdata = elf_getdata (symscn, NULL); - if (symdata == NULL || shdr->sh_entsize == 0) - /* The error has already been reported. */ - return; - - for (int cnt = 1; (size_t) cnt < shdr->sh_size / shdr->sh_entsize; ++cnt) - { - GElf_Versym versym_mem; - GElf_Versym *versym = gelf_getversym (data, cnt, &versym_mem); - if (versym == NULL) - { - ERROR (gettext ("\ -section [%2d] '%s': symbol %d: cannot read version data\n"), - idx, section_name (ebl, idx), cnt); - break; - } - - GElf_Sym sym_mem; - GElf_Sym *sym = gelf_getsym (symdata, cnt, &sym_mem); - if (sym == NULL) - /* Already reported elsewhere. */ - continue; - - if (*versym == VER_NDX_GLOBAL) - { - /* Global symbol. Make sure it is not defined as local. */ - if (GELF_ST_BIND (sym->st_info) == STB_LOCAL) - ERROR (gettext ("\ -section [%2d] '%s': symbol %d: local symbol with global scope\n"), - idx, section_name (ebl, idx), cnt); - } - else if (*versym != VER_NDX_LOCAL) - { - /* Versioned symbol. Make sure it is not defined as local. */ - if (!gnuld && GELF_ST_BIND (sym->st_info) == STB_LOCAL) - ERROR (gettext ("\ -section [%2d] '%s': symbol %d: local symbol with version\n"), - idx, section_name (ebl, idx), cnt); - - /* Look through the list of defined versions and locate the - index we need for this symbol. */ - struct version_namelist *runp = version_namelist; - while (runp != NULL) - if (runp->ndx == (*versym & (GElf_Versym) 0x7fff)) - break; - else - runp = runp->next; - - if (runp == NULL) - ERROR (gettext ("\ -section [%2d] '%s': symbol %d: invalid version index %d\n"), - idx, section_name (ebl, idx), cnt, (int) *versym); - else if (sym->st_shndx == SHN_UNDEF - && runp->type == ver_def) - ERROR (gettext ("\ -section [%2d] '%s': symbol %d: version index %d is for defined version\n"), - idx, section_name (ebl, idx), cnt, (int) *versym); - else if (sym->st_shndx != SHN_UNDEF - && runp->type == ver_need) - { - /* Unless this symbol has a copy relocation associated - this must not happen. */ - if (!has_copy_reloc (ebl, shdr->sh_link, cnt) - && !in_nobits_scn (ebl, sym->st_shndx)) - ERROR (gettext ("\ -section [%2d] '%s': symbol %d: version index %d is for requested version\n"), - idx, section_name (ebl, idx), cnt, (int) *versym); - } - } - } -} - - -static int -unknown_dependency_p (Elf *elf, const char *fname) -{ - GElf_Phdr phdr_mem; - GElf_Phdr *phdr = NULL; - - unsigned int i; - for (i = 0; i < phnum; ++i) - if ((phdr = gelf_getphdr (elf, i, &phdr_mem)) != NULL - && phdr->p_type == PT_DYNAMIC) - break; - - if (i == phnum) - return 1; - assert (phdr != NULL); - Elf_Scn *scn = gelf_offscn (elf, phdr->p_offset); - GElf_Shdr shdr_mem; - GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); - Elf_Data *data = elf_getdata (scn, NULL); - if (shdr != NULL && shdr->sh_type == SHT_DYNAMIC - && data != NULL && shdr->sh_entsize != 0) - for (size_t j = 0; j < shdr->sh_size / shdr->sh_entsize; ++j) - { - GElf_Dyn dyn_mem; - GElf_Dyn *dyn = gelf_getdyn (data, j, &dyn_mem); - if (dyn != NULL && dyn->d_tag == DT_NEEDED) - { - const char *str = elf_strptr (elf, shdr->sh_link, dyn->d_un.d_val); - if (str != NULL && strcmp (str, fname) == 0) - /* Found it. */ - return 0; - } - } - - return 1; -} - - -static unsigned int nverneed; - -static void -check_verneed (Ebl *ebl, GElf_Shdr *shdr, int idx) -{ - if (++nverneed == 2) - ERROR (gettext ("more than one version reference section present\n")); - - GElf_Shdr strshdr_mem; - GElf_Shdr *strshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), - &strshdr_mem); - if (strshdr == NULL) - return; - if (strshdr->sh_type != SHT_STRTAB) - ERROR (gettext ("\ -section [%2d] '%s': sh_link does not link to string table\n"), - idx, section_name (ebl, idx)); - - Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL); - if (data == NULL) - { - ERROR (gettext ("section [%2d] '%s': cannot get section data\n"), - idx, section_name (ebl, idx)); - return; - } - unsigned int offset = 0; - for (Elf64_Word cnt = shdr->sh_info; cnt > 0; ) - { - cnt--; - - /* Get the data at the next offset. */ - GElf_Verneed needmem; - GElf_Verneed *need = gelf_getverneed (data, offset, &needmem); - if (need == NULL) - break; - - unsigned int auxoffset = offset + need->vn_aux; - - if (need->vn_version != EV_CURRENT) - ERROR (gettext ("\ -section [%2d] '%s': entry %d has wrong version %d\n"), - idx, section_name (ebl, idx), cnt, (int) need->vn_version); - - if (need->vn_cnt > 0 && need->vn_aux < gelf_fsize (ebl->elf, ELF_T_VNEED, - 1, EV_CURRENT)) - { - ERROR (gettext ("\ -section [%2d] '%s': entry %d has wrong offset of auxiliary data\n"), - idx, section_name (ebl, idx), cnt); - break; - } - - const char *libname = elf_strptr (ebl->elf, shdr->sh_link, - need->vn_file); - if (libname == NULL) - { - ERROR (gettext ("\ -section [%2d] '%s': entry %d has invalid file reference\n"), - idx, section_name (ebl, idx), cnt); - goto next_need; - } - - /* Check that there is a DT_NEEDED entry for the referenced library. */ - if (unknown_dependency_p (ebl->elf, libname)) - ERROR (gettext ("\ -section [%2d] '%s': entry %d references unknown dependency\n"), - idx, section_name (ebl, idx), cnt); - - for (int cnt2 = need->vn_cnt; --cnt2 >= 0; ) - { - GElf_Vernaux auxmem; - GElf_Vernaux *aux = gelf_getvernaux (data, auxoffset, &auxmem); - if (aux == NULL) - break; - - if ((aux->vna_flags & ~VER_FLG_WEAK) != 0) - ERROR (gettext ("\ -section [%2d] '%s': auxiliary entry %d of entry %d has unknown flag\n"), - idx, section_name (ebl, idx), need->vn_cnt - cnt2, cnt); - - const char *verstr = elf_strptr (ebl->elf, shdr->sh_link, - aux->vna_name); - if (verstr == NULL) - { - ERROR (gettext ("\ -section [%2d] '%s': auxiliary entry %d of entry %d has invalid name reference\n"), - idx, section_name (ebl, idx), need->vn_cnt - cnt2, cnt); - break; - } - else - { - GElf_Word hashval = elf_hash (verstr); - if (hashval != aux->vna_hash) - ERROR (gettext ("\ -section [%2d] '%s': auxiliary entry %d of entry %d has wrong hash value: %#x, expected %#x\n"), - idx, section_name (ebl, idx), need->vn_cnt - cnt2, - cnt, (int) hashval, (int) aux->vna_hash); - - int res = add_version (libname, verstr, aux->vna_other, - ver_need); - if (unlikely (res !=0)) - { - ERROR (gettext ("\ -section [%2d] '%s': auxiliary entry %d of entry %d has duplicate version name '%s'\n"), - idx, section_name (ebl, idx), need->vn_cnt - cnt2, - cnt, verstr); - } - } - - if ((aux->vna_next != 0 || cnt2 > 0) - && aux->vna_next < gelf_fsize (ebl->elf, ELF_T_VNAUX, 1, - EV_CURRENT)) - { - ERROR (gettext ("\ -section [%2d] '%s': auxiliary entry %d of entry %d has wrong next field\n"), - idx, section_name (ebl, idx), need->vn_cnt - cnt2, cnt); - break; - } - - auxoffset += MAX (aux->vna_next, - gelf_fsize (ebl->elf, ELF_T_VNAUX, 1, EV_CURRENT)); - } - - /* Find the next offset. */ - next_need: - offset += need->vn_next; - - if ((need->vn_next != 0 || cnt > 0) - && offset < auxoffset) - { - ERROR (gettext ("\ -section [%2d] '%s': entry %d has invalid offset to next entry\n"), - idx, section_name (ebl, idx), cnt); - break; - } - - if (need->vn_next == 0 && cnt > 0) - { - ERROR (gettext ("\ -section [%2d] '%s': entry %d has zero offset to next entry, but sh_info says there are more entries\n"), - idx, section_name (ebl, idx), cnt); - break; - } - } -} - - -static unsigned int nverdef; - -static void -check_verdef (Ebl *ebl, GElf_Shdr *shdr, int idx) -{ - if (++nverdef == 2) - ERROR (gettext ("more than one version definition section present\n")); - - GElf_Shdr strshdr_mem; - GElf_Shdr *strshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), - &strshdr_mem); - if (strshdr == NULL) - return; - if (strshdr->sh_type != SHT_STRTAB) - ERROR (gettext ("\ -section [%2d] '%s': sh_link does not link to string table\n"), - idx, section_name (ebl, idx)); - - Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL); - if (data == NULL) - { - no_data: - ERROR (gettext ("section [%2d] '%s': cannot get section data\n"), - idx, section_name (ebl, idx)); - return; - } - - /* Iterate over all version definition entries. We check that there - is a BASE entry and that each index is unique. To do the later - we collection the information in a list which is later - examined. */ - struct namelist - { - const char *name; - struct namelist *next; - } *namelist = NULL; - struct namelist *refnamelist = NULL; - - bool has_base = false; - unsigned int offset = 0; - for (Elf64_Word cnt = shdr->sh_info; cnt > 0; ) - { - cnt--; - - /* Get the data at the next offset. */ - GElf_Verdef defmem; - GElf_Verdef *def = gelf_getverdef (data, offset, &defmem); - if (def == NULL) - goto no_data; - - if ((def->vd_flags & VER_FLG_BASE) != 0) - { - if (has_base) - ERROR (gettext ("\ -section [%2d] '%s': more than one BASE definition\n"), - idx, section_name (ebl, idx)); - if (def->vd_ndx != VER_NDX_GLOBAL) - ERROR (gettext ("\ -section [%2d] '%s': BASE definition must have index VER_NDX_GLOBAL\n"), - idx, section_name (ebl, idx)); - has_base = true; - } - if ((def->vd_flags & ~(VER_FLG_BASE|VER_FLG_WEAK)) != 0) - ERROR (gettext ("\ -section [%2d] '%s': entry %d has unknown flag\n"), - idx, section_name (ebl, idx), cnt); - - if (def->vd_version != EV_CURRENT) - ERROR (gettext ("\ -section [%2d] '%s': entry %d has wrong version %d\n"), - idx, section_name (ebl, idx), cnt, (int) def->vd_version); - - if (def->vd_cnt > 0 && def->vd_aux < gelf_fsize (ebl->elf, ELF_T_VDEF, - 1, EV_CURRENT)) - { - ERROR (gettext ("\ -section [%2d] '%s': entry %d has wrong offset of auxiliary data\n"), - idx, section_name (ebl, idx), cnt); - break; - } - - unsigned int auxoffset = offset + def->vd_aux; - GElf_Verdaux auxmem; - GElf_Verdaux *aux = gelf_getverdaux (data, auxoffset, &auxmem); - if (aux == NULL) - goto no_data; - - const char *name = elf_strptr (ebl->elf, shdr->sh_link, aux->vda_name); - if (name == NULL) - { - ERROR (gettext ("\ -section [%2d] '%s': entry %d has invalid name reference\n"), - idx, section_name (ebl, idx), cnt); - goto next_def; - } - GElf_Word hashval = elf_hash (name); - if (def->vd_hash != hashval) - ERROR (gettext ("\ -section [%2d] '%s': entry %d has wrong hash value: %#x, expected %#x\n"), - idx, section_name (ebl, idx), cnt, (int) hashval, - (int) def->vd_hash); - - int res = add_version (NULL, name, def->vd_ndx, ver_def); - if (unlikely (res !=0)) - { - ERROR (gettext ("\ -section [%2d] '%s': entry %d has duplicate version name '%s'\n"), - idx, section_name (ebl, idx), cnt, name); - } - - struct namelist *newname = alloca (sizeof (*newname)); - newname->name = name; - newname->next = namelist; - namelist = newname; - - auxoffset += aux->vda_next; - for (int cnt2 = 1; cnt2 < def->vd_cnt; ++cnt2) - { - aux = gelf_getverdaux (data, auxoffset, &auxmem); - if (aux == NULL) - goto no_data; - - name = elf_strptr (ebl->elf, shdr->sh_link, aux->vda_name); - if (name == NULL) - { - ERROR (gettext ("\ -section [%2d] '%s': entry %d has invalid name reference in auxiliary data\n"), - idx, section_name (ebl, idx), cnt); - break; - } - else - { - newname = alloca (sizeof (*newname)); - newname->name = name; - newname->next = refnamelist; - refnamelist = newname; - } - - if ((aux->vda_next != 0 || cnt2 + 1 < def->vd_cnt) - && aux->vda_next < gelf_fsize (ebl->elf, ELF_T_VDAUX, 1, - EV_CURRENT)) - { - ERROR (gettext ("\ -section [%2d] '%s': entry %d has wrong next field in auxiliary data\n"), - idx, section_name (ebl, idx), cnt); - break; - } - - auxoffset += MAX (aux->vda_next, - gelf_fsize (ebl->elf, ELF_T_VDAUX, 1, EV_CURRENT)); - } - - /* Find the next offset. */ - next_def: - offset += def->vd_next; - - if ((def->vd_next != 0 || cnt > 0) - && offset < auxoffset) - { - ERROR (gettext ("\ -section [%2d] '%s': entry %d has invalid offset to next entry\n"), - idx, section_name (ebl, idx), cnt); - break; - } - - if (def->vd_next == 0 && cnt > 0) - { - ERROR (gettext ("\ -section [%2d] '%s': entry %d has zero offset to next entry, but sh_info says there are more entries\n"), - idx, section_name (ebl, idx), cnt); - break; - } - } - - if (!has_base) - ERROR (gettext ("section [%2d] '%s': no BASE definition\n"), - idx, section_name (ebl, idx)); - - /* Check whether the referenced names are available. */ - while (namelist != NULL) - { - struct version_namelist *runp = version_namelist; - while (runp != NULL) - { - if (runp->type == ver_def - && strcmp (runp->name, namelist->name) == 0) - break; - runp = runp->next; - } - - if (runp == NULL) - ERROR (gettext ("\ -section [%2d] '%s': unknown parent version '%s'\n"), - idx, section_name (ebl, idx), namelist->name); - - namelist = namelist->next; - } -} - -static void -check_attributes (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) -{ - if (shdr->sh_size == 0) - { - ERROR (gettext ("section [%2d] '%s': empty object attributes section\n"), - idx, section_name (ebl, idx)); - return; - } - - Elf_Data *data = elf_rawdata (elf_getscn (ebl->elf, idx), NULL); - if (data == NULL || data->d_size == 0 || data->d_buf == NULL) - { - ERROR (gettext ("section [%2d] '%s': cannot get section data\n"), - idx, section_name (ebl, idx)); - return; - } - - inline size_t pos (const unsigned char *p) - { - return p - (const unsigned char *) data->d_buf; - } - - const unsigned char *p = data->d_buf; - if (*p++ != 'A') - { - ERROR (gettext ("section [%2d] '%s': unrecognized attribute format\n"), - idx, section_name (ebl, idx)); - return; - } - - inline size_t left (void) - { - return (const unsigned char *) data->d_buf + data->d_size - p; - } - - while (left () >= 4) - { - uint32_t len; - memcpy (&len, p, sizeof len); - - if (len == 0) - ERROR (gettext ("\ -section [%2d] '%s': offset %zu: zero length field in attribute section\n"), - idx, section_name (ebl, idx), pos (p)); - - if (MY_ELFDATA != ehdr->e_ident[EI_DATA]) - CONVERT (len); - - if (len > left ()) - { - ERROR (gettext ("\ -section [%2d] '%s': offset %zu: invalid length in attribute section\n"), - idx, section_name (ebl, idx), pos (p)); - break; - } - - const unsigned char *name = p + sizeof len; - p += len; - - unsigned const char *q = memchr (name, '\0', len); - if (q == NULL) - { - ERROR (gettext ("\ -section [%2d] '%s': offset %zu: unterminated vendor name string\n"), - idx, section_name (ebl, idx), pos (p)); - break; - } - ++q; - - if (q - name == sizeof "gnu" && !memcmp (name, "gnu", sizeof "gnu")) - while (q < p) - { - unsigned const char *chunk = q; - - unsigned int subsection_tag; - get_uleb128 (subsection_tag, q, p); - - if (q >= p) - { - ERROR (gettext ("\ -section [%2d] '%s': offset %zu: endless ULEB128 in attribute subsection tag\n"), - idx, section_name (ebl, idx), pos (chunk)); - break; - } - - uint32_t subsection_len; - if (p - q < (ptrdiff_t) sizeof subsection_len) - { - ERROR (gettext ("\ -section [%2d] '%s': offset %zu: truncated attribute section\n"), - idx, section_name (ebl, idx), pos (q)); - break; - } - - memcpy (&subsection_len, q, sizeof subsection_len); - if (subsection_len == 0) - { - ERROR (gettext ("\ -section [%2d] '%s': offset %zu: zero length field in attribute subsection\n"), - idx, section_name (ebl, idx), pos (q)); - - q += sizeof subsection_len; - continue; - } - - if (MY_ELFDATA != ehdr->e_ident[EI_DATA]) - CONVERT (subsection_len); - - /* Don't overflow, ptrdiff_t might be 32bits, but signed. */ - if (p - chunk < (ptrdiff_t) subsection_len - || subsection_len >= (uint32_t) PTRDIFF_MAX) - { - ERROR (gettext ("\ -section [%2d] '%s': offset %zu: invalid length in attribute subsection\n"), - idx, section_name (ebl, idx), pos (q)); - break; - } - - const unsigned char *subsection_end = chunk + subsection_len; - chunk = q; - q = subsection_end; - - if (subsection_tag != 1) /* Tag_File */ - ERROR (gettext ("\ -section [%2d] '%s': offset %zu: attribute subsection has unexpected tag %u\n"), - idx, section_name (ebl, idx), pos (chunk), subsection_tag); - else - { - chunk += sizeof subsection_len; - while (chunk < q) - { - unsigned int tag; - get_uleb128 (tag, chunk, q); - - uint64_t value = 0; - const unsigned char *r = chunk; - if (tag == 32 || (tag & 1) == 0) - { - get_uleb128 (value, r, q); - if (r > q) - { - ERROR (gettext ("\ -section [%2d] '%s': offset %zu: endless ULEB128 in attribute tag\n"), - idx, section_name (ebl, idx), pos (chunk)); - break; - } - } - if (tag == 32 || (tag & 1) != 0) - { - r = memchr (r, '\0', q - r); - if (r == NULL) - { - ERROR (gettext ("\ -section [%2d] '%s': offset %zu: unterminated string in attribute\n"), - idx, section_name (ebl, idx), pos (chunk)); - break; - } - ++r; - } - - const char *tag_name = NULL; - const char *value_name = NULL; - if (!ebl_check_object_attribute (ebl, (const char *) name, - tag, value, - &tag_name, &value_name)) - ERROR (gettext ("\ -section [%2d] '%s': offset %zu: unrecognized attribute tag %u\n"), - idx, section_name (ebl, idx), pos (chunk), tag); - else if ((tag & 1) == 0 && value_name == NULL) - ERROR (gettext ("\ -section [%2d] '%s': offset %zu: unrecognized %s attribute value %" PRIu64 "\n"), - idx, section_name (ebl, idx), pos (chunk), - tag_name, value); - - chunk = r; - } - } - } - else - ERROR (gettext ("\ -section [%2d] '%s': offset %zu: vendor '%s' unknown\n"), - idx, section_name (ebl, idx), pos (p), name); - } - - if (left () != 0) - ERROR (gettext ("\ -section [%2d] '%s': offset %zu: extra bytes after last attribute section\n"), - idx, section_name (ebl, idx), pos (p)); -} - -static bool has_loadable_segment; -static bool has_interp_segment; - -static const struct -{ - const char *name; - size_t namelen; - GElf_Word type; - enum { unused, exact, atleast, exact_or_gnuld } attrflag; - GElf_Word attr; - GElf_Word attr2; -} special_sections[] = - { - /* See figure 4-14 in the gABI. */ - { ".bss", 5, SHT_NOBITS, exact, SHF_ALLOC | SHF_WRITE, 0 }, - { ".comment", 8, SHT_PROGBITS, atleast, 0, SHF_MERGE | SHF_STRINGS }, - { ".data", 6, SHT_PROGBITS, exact, SHF_ALLOC | SHF_WRITE, 0 }, - { ".data1", 7, SHT_PROGBITS, exact, SHF_ALLOC | SHF_WRITE, 0 }, - { ".debug_str", 11, SHT_PROGBITS, exact_or_gnuld, SHF_MERGE | SHF_STRINGS, 0 }, - { ".debug", 6, SHT_PROGBITS, exact, 0, 0 }, - { ".dynamic", 9, SHT_DYNAMIC, atleast, SHF_ALLOC, SHF_WRITE }, - { ".dynstr", 8, SHT_STRTAB, exact, SHF_ALLOC, 0 }, - { ".dynsym", 8, SHT_DYNSYM, exact, SHF_ALLOC, 0 }, - { ".fini", 6, SHT_PROGBITS, exact, SHF_ALLOC | SHF_EXECINSTR, 0 }, - { ".fini_array", 12, SHT_FINI_ARRAY, exact, SHF_ALLOC | SHF_WRITE, 0 }, - { ".got", 5, SHT_PROGBITS, unused, 0, 0 }, // XXX more info? - { ".hash", 6, SHT_HASH, exact, SHF_ALLOC, 0 }, - { ".init", 6, SHT_PROGBITS, exact, SHF_ALLOC | SHF_EXECINSTR, 0 }, - { ".init_array", 12, SHT_INIT_ARRAY, exact, SHF_ALLOC | SHF_WRITE, 0 }, - { ".interp", 8, SHT_PROGBITS, atleast, 0, SHF_ALLOC }, // XXX more tests? - { ".line", 6, SHT_PROGBITS, exact, 0, 0 }, - { ".note", 6, SHT_NOTE, atleast, 0, SHF_ALLOC }, - { ".plt", 5, SHT_PROGBITS, unused, 0, 0 }, // XXX more tests - { ".preinit_array", 15, SHT_PREINIT_ARRAY, exact, SHF_ALLOC | SHF_WRITE, 0 }, - { ".rela", 5, SHT_RELA, atleast, 0, SHF_ALLOC | SHF_INFO_LINK }, // XXX more tests - { ".rel", 4, SHT_REL, atleast, 0, SHF_ALLOC | SHF_INFO_LINK }, // XXX more tests - { ".rodata", 8, SHT_PROGBITS, atleast, SHF_ALLOC, SHF_MERGE | SHF_STRINGS }, - { ".rodata1", 9, SHT_PROGBITS, atleast, SHF_ALLOC, SHF_MERGE | SHF_STRINGS }, - { ".shstrtab", 10, SHT_STRTAB, exact, 0, 0 }, - { ".strtab", 8, SHT_STRTAB, atleast, 0, SHF_ALLOC }, // XXX more tests - { ".symtab", 8, SHT_SYMTAB, atleast, 0, SHF_ALLOC }, // XXX more tests - { ".symtab_shndx", 14, SHT_SYMTAB_SHNDX, atleast, 0, SHF_ALLOC }, // XXX more tests - { ".tbss", 6, SHT_NOBITS, exact, SHF_ALLOC | SHF_WRITE | SHF_TLS, 0 }, - { ".tdata", 7, SHT_PROGBITS, exact, SHF_ALLOC | SHF_WRITE | SHF_TLS, 0 }, - { ".tdata1", 8, SHT_PROGBITS, exact, SHF_ALLOC | SHF_WRITE | SHF_TLS, 0 }, - { ".text", 6, SHT_PROGBITS, exact, SHF_ALLOC | SHF_EXECINSTR, 0 }, - - /* The following are GNU extensions. */ - { ".gnu.version", 13, SHT_GNU_versym, exact, SHF_ALLOC, 0 }, - { ".gnu.version_d", 15, SHT_GNU_verdef, exact, SHF_ALLOC, 0 }, - { ".gnu.version_r", 15, SHT_GNU_verneed, exact, SHF_ALLOC, 0 }, - { ".gnu.attributes", 16, SHT_GNU_ATTRIBUTES, exact, 0, 0 }, - }; -#define nspecial_sections \ - (sizeof (special_sections) / sizeof (special_sections[0])) - -#define IS_KNOWN_SPECIAL(idx, string, prefix) \ - (special_sections[idx].namelen == sizeof string - (prefix ? 1 : 0) \ - && !memcmp (special_sections[idx].name, string, \ - sizeof string - (prefix ? 1 : 0))) - - -/* Indeces of some sections we need later. */ -static size_t eh_frame_hdr_scnndx; -static size_t eh_frame_scnndx; -static size_t gcc_except_table_scnndx; - - -static void -check_sections (Ebl *ebl, GElf_Ehdr *ehdr) -{ - if (ehdr->e_shoff == 0) - /* No section header. */ - return; - - /* Allocate array to count references in section groups. */ - scnref = (int *) xcalloc (shnum, sizeof (int)); - - /* Check the zeroth section first. It must not have any contents - and the section header must contain nonzero value at most in the - sh_size and sh_link fields. */ - GElf_Shdr shdr_mem; - GElf_Shdr *shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem); - if (shdr == NULL) - ERROR (gettext ("cannot get section header of zeroth section\n")); - else - { - if (shdr->sh_name != 0) - ERROR (gettext ("zeroth section has nonzero name\n")); - if (shdr->sh_type != 0) - ERROR (gettext ("zeroth section has nonzero type\n")); - if (shdr->sh_flags != 0) - ERROR (gettext ("zeroth section has nonzero flags\n")); - if (shdr->sh_addr != 0) - ERROR (gettext ("zeroth section has nonzero address\n")); - if (shdr->sh_offset != 0) - ERROR (gettext ("zeroth section has nonzero offset\n")); - if (shdr->sh_addralign != 0) - ERROR (gettext ("zeroth section has nonzero align value\n")); - if (shdr->sh_entsize != 0) - ERROR (gettext ("zeroth section has nonzero entry size value\n")); - - if (shdr->sh_size != 0 && ehdr->e_shnum != 0) - ERROR (gettext ("\ -zeroth section has nonzero size value while ELF header has nonzero shnum value\n")); - - if (shdr->sh_link != 0 && ehdr->e_shstrndx != SHN_XINDEX) - ERROR (gettext ("\ -zeroth section has nonzero link value while ELF header does not signal overflow in shstrndx\n")); - - if (shdr->sh_info != 0 && ehdr->e_phnum != PN_XNUM) - ERROR (gettext ("\ -zeroth section has nonzero link value while ELF header does not signal overflow in phnum\n")); - } - - int *segment_flags = xcalloc (phnum, sizeof segment_flags[0]); - - bool dot_interp_section = false; - - size_t hash_idx = 0; - size_t gnu_hash_idx = 0; - - size_t versym_scnndx = 0; - for (size_t cnt = 1; cnt < shnum; ++cnt) - { - Elf_Scn *scn = elf_getscn (ebl->elf, cnt); - shdr = gelf_getshdr (scn, &shdr_mem); - if (shdr == NULL) - { - ERROR (gettext ("\ -cannot get section header for section [%2zu] '%s': %s\n"), - cnt, section_name (ebl, cnt), elf_errmsg (-1)); - continue; - } - - const char *scnname = elf_strptr (ebl->elf, shstrndx, shdr->sh_name); - - if (scnname == NULL) - ERROR (gettext ("section [%2zu]: invalid name\n"), cnt); - else - { - /* Check whether it is one of the special sections defined in - the gABI. */ - size_t s; - for (s = 0; s < nspecial_sections; ++s) - if (strncmp (scnname, special_sections[s].name, - special_sections[s].namelen) == 0) - { - char stbuf1[100]; - char stbuf2[100]; - char stbuf3[100]; - - GElf_Word good_type = special_sections[s].type; - if (IS_KNOWN_SPECIAL (s, ".plt", false) - && ebl_bss_plt_p (ebl)) - good_type = SHT_NOBITS; - - /* In a debuginfo file, any normal section can be SHT_NOBITS. - This is only invalid for DWARF sections and .shstrtab. */ - if (shdr->sh_type != good_type - && (shdr->sh_type != SHT_NOBITS - || !is_debuginfo - || IS_KNOWN_SPECIAL (s, ".debug_str", false) - || IS_KNOWN_SPECIAL (s, ".debug", true) - || IS_KNOWN_SPECIAL (s, ".shstrtab", false))) - ERROR (gettext ("\ -section [%2d] '%s' has wrong type: expected %s, is %s\n"), - (int) cnt, scnname, - ebl_section_type_name (ebl, special_sections[s].type, - stbuf1, sizeof (stbuf1)), - ebl_section_type_name (ebl, shdr->sh_type, - stbuf2, sizeof (stbuf2))); - - if (special_sections[s].attrflag == exact - || special_sections[s].attrflag == exact_or_gnuld) - { - /* Except for the link order, group bit and - compression flag all the other bits should - match exactly. */ - if ((shdr->sh_flags - & ~(SHF_LINK_ORDER | SHF_GROUP | SHF_COMPRESSED)) - != special_sections[s].attr - && (special_sections[s].attrflag == exact || !gnuld)) - ERROR (gettext ("\ -section [%2zu] '%s' has wrong flags: expected %s, is %s\n"), - cnt, scnname, - section_flags_string (special_sections[s].attr, - stbuf1, sizeof (stbuf1)), - section_flags_string (shdr->sh_flags - & ~SHF_LINK_ORDER, - stbuf2, sizeof (stbuf2))); - } - else if (special_sections[s].attrflag == atleast) - { - if ((shdr->sh_flags & special_sections[s].attr) - != special_sections[s].attr - || ((shdr->sh_flags - & ~(SHF_LINK_ORDER | SHF_GROUP | SHF_COMPRESSED - | special_sections[s].attr - | special_sections[s].attr2)) - != 0)) - ERROR (gettext ("\ -section [%2zu] '%s' has wrong flags: expected %s and possibly %s, is %s\n"), - cnt, scnname, - section_flags_string (special_sections[s].attr, - stbuf1, sizeof (stbuf1)), - section_flags_string (special_sections[s].attr2, - stbuf2, sizeof (stbuf2)), - section_flags_string (shdr->sh_flags - & ~(SHF_LINK_ORDER - | SHF_GROUP), - stbuf3, sizeof (stbuf3))); - } - - if (strcmp (scnname, ".interp") == 0) - { - dot_interp_section = true; - - if (ehdr->e_type == ET_REL) - ERROR (gettext ("\ -section [%2zu] '%s' present in object file\n"), - cnt, scnname); - - if ((shdr->sh_flags & SHF_ALLOC) != 0 - && !has_loadable_segment) - ERROR (gettext ("\ -section [%2zu] '%s' has SHF_ALLOC flag set but there is no loadable segment\n"), - cnt, scnname); - else if ((shdr->sh_flags & SHF_ALLOC) == 0 - && has_loadable_segment) - ERROR (gettext ("\ -section [%2zu] '%s' has SHF_ALLOC flag not set but there are loadable segments\n"), - cnt, scnname); - } - else - { - if (strcmp (scnname, ".symtab_shndx") == 0 - && ehdr->e_type != ET_REL) - ERROR (gettext ("\ -section [%2zu] '%s' is extension section index table in non-object file\n"), - cnt, scnname); - - /* These sections must have the SHF_ALLOC flag set iff - a loadable segment is available. - - .relxxx - .strtab - .symtab - .symtab_shndx - - Check that if there is a reference from the - loaded section these sections also have the - ALLOC flag set. */ -#if 0 - // XXX TODO - if ((shdr->sh_flags & SHF_ALLOC) != 0 - && !has_loadable_segment) - ERROR (gettext ("\ -section [%2zu] '%s' has SHF_ALLOC flag set but there is no loadable segment\n"), - cnt, scnname); - else if ((shdr->sh_flags & SHF_ALLOC) == 0 - && has_loadable_segment) - ERROR (gettext ("\ -section [%2zu] '%s' has SHF_ALLOC flag not set but there are loadable segments\n"), - cnt, scnname); -#endif - } - - break; - } - - /* Remember a few special sections for later. */ - if (strcmp (scnname, ".eh_frame_hdr") == 0) - eh_frame_hdr_scnndx = cnt; - else if (strcmp (scnname, ".eh_frame") == 0) - eh_frame_scnndx = cnt; - else if (strcmp (scnname, ".gcc_except_table") == 0) - gcc_except_table_scnndx = cnt; - } - - if (shdr->sh_entsize != 0 && shdr->sh_size % shdr->sh_entsize) - ERROR (gettext ("\ -section [%2zu] '%s': size not multiple of entry size\n"), - cnt, section_name (ebl, cnt)); - - if (elf_strptr (ebl->elf, shstrndx, shdr->sh_name) == NULL) - ERROR (gettext ("cannot get section header\n")); - - if (shdr->sh_type >= SHT_NUM - && shdr->sh_type != SHT_GNU_ATTRIBUTES - && shdr->sh_type != SHT_GNU_LIBLIST - && shdr->sh_type != SHT_CHECKSUM - && shdr->sh_type != SHT_GNU_verdef - && shdr->sh_type != SHT_GNU_verneed - && shdr->sh_type != SHT_GNU_versym - && ebl_section_type_name (ebl, shdr->sh_type, NULL, 0) == NULL) - ERROR (gettext ("section [%2zu] '%s' has unsupported type %d\n"), - cnt, section_name (ebl, cnt), - (int) shdr->sh_type); - -#define ALL_SH_FLAGS (SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR | SHF_MERGE \ - | SHF_STRINGS | SHF_INFO_LINK | SHF_LINK_ORDER \ - | SHF_OS_NONCONFORMING | SHF_GROUP | SHF_TLS \ - | SHF_COMPRESSED) - if (shdr->sh_flags & ~(GElf_Xword) ALL_SH_FLAGS) - { - GElf_Xword sh_flags = shdr->sh_flags & ~(GElf_Xword) ALL_SH_FLAGS; - if (sh_flags & SHF_MASKPROC) - { - if (!ebl_machine_section_flag_check (ebl, - sh_flags & SHF_MASKPROC)) - ERROR (gettext ("section [%2zu] '%s'" - " contains invalid processor-specific flag(s)" - " %#" PRIx64 "\n"), - cnt, section_name (ebl, cnt), sh_flags & SHF_MASKPROC); - sh_flags &= ~(GElf_Xword) SHF_MASKPROC; - } - if (sh_flags != 0) - ERROR (gettext ("section [%2zu] '%s' contains unknown flag(s)" - " %#" PRIx64 "\n"), - cnt, section_name (ebl, cnt), sh_flags); - } - if (shdr->sh_flags & SHF_TLS) - { - // XXX Correct? - if (shdr->sh_addr != 0 && !gnuld) - ERROR (gettext ("\ -section [%2zu] '%s': thread-local data sections address not zero\n"), - cnt, section_name (ebl, cnt)); - - // XXX TODO more tests!? - } - - if (shdr->sh_flags & SHF_COMPRESSED) - { - if (shdr->sh_flags & SHF_ALLOC) - ERROR (gettext ("\ -section [%2zu] '%s': allocated section cannot be compressed\n"), - cnt, section_name (ebl, cnt)); - - if (shdr->sh_type == SHT_NOBITS) - ERROR (gettext ("\ -section [%2zu] '%s': nobits section cannot be compressed\n"), - cnt, section_name (ebl, cnt)); - - GElf_Chdr chdr; - if (gelf_getchdr (scn, &chdr) == NULL) - ERROR (gettext ("\ -section [%2zu] '%s': compressed section with no compression header: %s\n"), - cnt, section_name (ebl, cnt), elf_errmsg (-1)); - } - - if (shdr->sh_link >= shnum) - ERROR (gettext ("\ -section [%2zu] '%s': invalid section reference in link value\n"), - cnt, section_name (ebl, cnt)); - - if (SH_INFO_LINK_P (shdr) && shdr->sh_info >= shnum) - ERROR (gettext ("\ -section [%2zu] '%s': invalid section reference in info value\n"), - cnt, section_name (ebl, cnt)); - - if ((shdr->sh_flags & SHF_MERGE) == 0 - && (shdr->sh_flags & SHF_STRINGS) != 0 - && be_strict) - ERROR (gettext ("\ -section [%2zu] '%s': strings flag set without merge flag\n"), - cnt, section_name (ebl, cnt)); - - if ((shdr->sh_flags & SHF_MERGE) != 0 && shdr->sh_entsize == 0) - ERROR (gettext ("\ -section [%2zu] '%s': merge flag set but entry size is zero\n"), - cnt, section_name (ebl, cnt)); - - if (shdr->sh_flags & SHF_GROUP) - check_scn_group (ebl, cnt); - - if (shdr->sh_flags & SHF_EXECINSTR) - { - switch (shdr->sh_type) - { - case SHT_PROGBITS: - break; - - case SHT_NOBITS: - if (is_debuginfo) - break; - FALLTHROUGH; - default: - ERROR (gettext ("\ -section [%2zu] '%s' has unexpected type %d for an executable section\n"), - cnt, section_name (ebl, cnt), shdr->sh_type); - break; - } - - if (shdr->sh_flags & SHF_WRITE) - { - if (is_debuginfo && shdr->sh_type != SHT_NOBITS) - ERROR (gettext ("\ -section [%2zu] '%s' must be of type NOBITS in debuginfo files\n"), - cnt, section_name (ebl, cnt)); - - if (!is_debuginfo - && !ebl_check_special_section (ebl, cnt, shdr, - section_name (ebl, cnt))) - ERROR (gettext ("\ -section [%2zu] '%s' is both executable and writable\n"), - cnt, section_name (ebl, cnt)); - } - } - - if (ehdr->e_type != ET_REL && (shdr->sh_flags & SHF_ALLOC) != 0 - && !is_debuginfo) - { - /* Make sure the section is contained in a loaded segment - and that the initialization part matches NOBITS sections. */ - unsigned int pcnt; - GElf_Phdr phdr_mem; - GElf_Phdr *phdr; - - for (pcnt = 0; pcnt < phnum; ++pcnt) - if ((phdr = gelf_getphdr (ebl->elf, pcnt, &phdr_mem)) != NULL - && ((phdr->p_type == PT_LOAD - && (shdr->sh_flags & SHF_TLS) == 0) - || (phdr->p_type == PT_TLS - && (shdr->sh_flags & SHF_TLS) != 0)) - && phdr->p_offset <= shdr->sh_offset - && ((shdr->sh_offset - phdr->p_offset <= phdr->p_filesz - && (shdr->sh_offset - phdr->p_offset < phdr->p_filesz - || shdr->sh_size == 0)) - || (shdr->sh_offset - phdr->p_offset < phdr->p_memsz - && shdr->sh_type == SHT_NOBITS))) - { - /* Found the segment. */ - if (phdr->p_offset + phdr->p_memsz - < shdr->sh_offset + shdr->sh_size) - ERROR (gettext ("\ -section [%2zu] '%s' not fully contained in segment of program header entry %d\n"), - cnt, section_name (ebl, cnt), pcnt); - - if (shdr->sh_type == SHT_NOBITS) - { - if (shdr->sh_offset < phdr->p_offset + phdr->p_filesz - && !is_debuginfo) - { - if (!gnuld) - ERROR (gettext ("\ -section [%2zu] '%s' has type NOBITS but is read from the file in segment of program header entry %d\n"), - cnt, section_name (ebl, cnt), pcnt); - else - { - /* This is truly horrible. GNU ld might put a - NOBITS section in the middle of a PT_LOAD - segment, assuming the next gap in the file - actually consists of zero bits... - So it really is like a PROGBITS section - where the data is all zeros. Check those - zero bytes are really there. */ - bool bad; - Elf_Data *databits; - databits = elf_getdata_rawchunk (ebl->elf, - shdr->sh_offset, - shdr->sh_size, - ELF_T_BYTE); - bad = (databits == NULL - || databits->d_size != shdr->sh_size); - for (size_t idx = 0; - idx < databits->d_size && ! bad; - idx++) - bad = ((char *) databits->d_buf)[idx] != 0; - - if (bad) - ERROR (gettext ("\ -section [%2zu] '%s' has type NOBITS but is read from the file in segment of program header entry %d and file contents is non-zero\n"), - cnt, section_name (ebl, cnt), pcnt); - } - } - } - else - { - const GElf_Off end = phdr->p_offset + phdr->p_filesz; - if (shdr->sh_offset > end || - (shdr->sh_offset == end && shdr->sh_size != 0)) - ERROR (gettext ("\ -section [%2zu] '%s' has not type NOBITS but is not read from the file in segment of program header entry %d\n"), - cnt, section_name (ebl, cnt), pcnt); - } - - if (shdr->sh_type != SHT_NOBITS) - { - if ((shdr->sh_flags & SHF_EXECINSTR) != 0) - { - segment_flags[pcnt] |= PF_X; - if ((phdr->p_flags & PF_X) == 0) - ERROR (gettext ("\ -section [%2zu] '%s' is executable in nonexecutable segment %d\n"), - cnt, section_name (ebl, cnt), pcnt); - } - - if ((shdr->sh_flags & SHF_WRITE) != 0) - { - segment_flags[pcnt] |= PF_W; - if (0 /* XXX vdso images have this */ - && (phdr->p_flags & PF_W) == 0) - ERROR (gettext ("\ -section [%2zu] '%s' is writable in unwritable segment %d\n"), - cnt, section_name (ebl, cnt), pcnt); - } - } - - break; - } - - if (pcnt == phnum) - ERROR (gettext ("\ -section [%2zu] '%s': alloc flag set but section not in any loaded segment\n"), - cnt, section_name (ebl, cnt)); - } - - if (cnt == shstrndx && shdr->sh_type != SHT_STRTAB) - ERROR (gettext ("\ -section [%2zu] '%s': ELF header says this is the section header string table but type is not SHT_TYPE\n"), - cnt, section_name (ebl, cnt)); - - switch (shdr->sh_type) - { - case SHT_DYNSYM: - if (ehdr->e_type == ET_REL) - ERROR (gettext ("\ -section [%2zu] '%s': relocatable files cannot have dynamic symbol tables\n"), - cnt, section_name (ebl, cnt)); - FALLTHROUGH; - case SHT_SYMTAB: - check_symtab (ebl, ehdr, shdr, cnt); - break; - - case SHT_RELA: - check_rela (ebl, ehdr, shdr, cnt); - break; - - case SHT_REL: - check_rel (ebl, ehdr, shdr, cnt); - break; - - case SHT_DYNAMIC: - check_dynamic (ebl, ehdr, shdr, cnt); - break; - - case SHT_SYMTAB_SHNDX: - check_symtab_shndx (ebl, ehdr, shdr, cnt); - break; - - case SHT_HASH: - check_hash (shdr->sh_type, ebl, ehdr, shdr, cnt); - hash_idx = cnt; - break; - - case SHT_GNU_HASH: - check_hash (shdr->sh_type, ebl, ehdr, shdr, cnt); - gnu_hash_idx = cnt; - break; - - case SHT_NULL: - check_null (ebl, shdr, cnt); - break; - - case SHT_GROUP: - check_group (ebl, ehdr, shdr, cnt); - break; - - case SHT_NOTE: - check_note_section (ebl, ehdr, shdr, cnt); - break; - - case SHT_GNU_versym: - /* We cannot process this section now since we have no guarantee - that the verneed and verdef sections have already been read. - Just remember the section index. */ - if (versym_scnndx != 0) - ERROR (gettext ("more than one version symbol table present\n")); - versym_scnndx = cnt; - break; - - case SHT_GNU_verneed: - check_verneed (ebl, shdr, cnt); - break; - - case SHT_GNU_verdef: - check_verdef (ebl, shdr, cnt); - break; - - case SHT_GNU_ATTRIBUTES: - check_attributes (ebl, ehdr, shdr, cnt); - break; - - default: - /* Nothing. */ - break; - } - } - - if (has_interp_segment && !dot_interp_section) - ERROR (gettext ("INTERP program header entry but no .interp section\n")); - - if (!is_debuginfo) - for (unsigned int pcnt = 0; pcnt < phnum; ++pcnt) - { - GElf_Phdr phdr_mem; - GElf_Phdr *phdr = gelf_getphdr (ebl->elf, pcnt, &phdr_mem); - if (phdr != NULL && (phdr->p_type == PT_LOAD || phdr->p_type == PT_TLS)) - { - if ((phdr->p_flags & PF_X) != 0 - && (segment_flags[pcnt] & PF_X) == 0) - ERROR (gettext ("\ -loadable segment [%u] is executable but contains no executable sections\n"), - pcnt); - - if ((phdr->p_flags & PF_W) != 0 - && (segment_flags[pcnt] & PF_W) == 0) - ERROR (gettext ("\ -loadable segment [%u] is writable but contains no writable sections\n"), - pcnt); - } - } - - free (segment_flags); - - if (version_namelist != NULL) - { - if (versym_scnndx == 0) - ERROR (gettext ("\ -no .gnu.versym section present but .gnu.versym_d or .gnu.versym_r section exist\n")); - else - check_versym (ebl, versym_scnndx); - - /* Check for duplicate index numbers. */ - do - { - struct version_namelist *runp = version_namelist->next; - while (runp != NULL) - { - if (version_namelist->ndx == runp->ndx) - { - ERROR (gettext ("duplicate version index %d\n"), - (int) version_namelist->ndx); - break; - } - runp = runp->next; - } - - struct version_namelist *old = version_namelist; - version_namelist = version_namelist->next; - free (old); - } - while (version_namelist != NULL); - } - else if (versym_scnndx != 0) - ERROR (gettext ("\ -.gnu.versym section present without .gnu.versym_d or .gnu.versym_r\n")); - - if (hash_idx != 0 && gnu_hash_idx != 0) - compare_hash_gnu_hash (ebl, ehdr, hash_idx, gnu_hash_idx); - - free (scnref); -} - - -static GElf_Off -check_note_data (Ebl *ebl, const GElf_Ehdr *ehdr, - Elf_Data *data, int shndx, int phndx, GElf_Off start) -{ - size_t offset = 0; - size_t last_offset = 0; - GElf_Nhdr nhdr; - size_t name_offset; - size_t desc_offset; - while (offset < data->d_size - && (offset = gelf_getnote (data, offset, - &nhdr, &name_offset, &desc_offset)) > 0) - { - last_offset = offset; - - /* Make sure it is one of the note types we know about. */ - if (ehdr->e_type == ET_CORE) - switch (nhdr.n_type) - { - case NT_PRSTATUS: - case NT_FPREGSET: - case NT_PRPSINFO: - case NT_TASKSTRUCT: /* NT_PRXREG on Solaris. */ - case NT_PLATFORM: - case NT_AUXV: - case NT_GWINDOWS: - case NT_ASRS: - case NT_PSTATUS: - case NT_PSINFO: - case NT_PRCRED: - case NT_UTSNAME: - case NT_LWPSTATUS: - case NT_LWPSINFO: - case NT_PRFPXREG: - /* Known type. */ - break; - - default: - if (shndx == 0) - ERROR (gettext ("\ -phdr[%d]: unknown core file note type %" PRIu32 " at offset %" PRIu64 "\n"), - phndx, (uint32_t) nhdr.n_type, start + offset); - else - ERROR (gettext ("\ -section [%2d] '%s': unknown core file note type %" PRIu32 - " at offset %zu\n"), - shndx, section_name (ebl, shndx), - (uint32_t) nhdr.n_type, offset); - } - else - switch (nhdr.n_type) - { - case NT_GNU_ABI_TAG: - case NT_GNU_HWCAP: - case NT_GNU_BUILD_ID: - case NT_GNU_GOLD_VERSION: - case NT_GNU_PROPERTY_TYPE_0: - if (nhdr.n_namesz == sizeof ELF_NOTE_GNU - && strcmp (data->d_buf + name_offset, ELF_NOTE_GNU) == 0) - break; - else - { - /* NT_VERSION is 1, same as NT_GNU_ABI_TAG. It has no - descriptor and (ab)uses the name as version string. */ - if (nhdr.n_descsz == 0 && nhdr.n_type == NT_VERSION) - break; - } - goto unknown_note; - - case NT_GNU_BUILD_ATTRIBUTE_OPEN: - case NT_GNU_BUILD_ATTRIBUTE_FUNC: - /* GNU Build Attributes store most data in the owner - name, which must start with the - ELF_NOTE_GNU_BUILD_ATTRIBUTE_PREFIX "GA". */ - if (nhdr.n_namesz >= sizeof ELF_NOTE_GNU_BUILD_ATTRIBUTE_PREFIX - && strncmp (data->d_buf + name_offset, - ELF_NOTE_GNU_BUILD_ATTRIBUTE_PREFIX, - strlen (ELF_NOTE_GNU_BUILD_ATTRIBUTE_PREFIX)) == 0) - break; - else - goto unknown_note; - - case 0: - /* Linux vDSOs use a type 0 note for the kernel version word. */ - if (nhdr.n_namesz == sizeof "Linux" - && !memcmp (data->d_buf + name_offset, "Linux", sizeof "Linux")) - break; - FALLTHROUGH; - default: - { - unknown_note: - if (shndx == 0) - ERROR (gettext ("\ -phdr[%d]: unknown object file note type %" PRIu32 " with owner name '%s' at offset %zu\n"), - phndx, (uint32_t) nhdr.n_type, - (char *) data->d_buf + name_offset, offset); - else - ERROR (gettext ("\ -section [%2d] '%s': unknown object file note type %" PRIu32 - " with owner name '%s' at offset %zu\n"), - shndx, section_name (ebl, shndx), - (uint32_t) nhdr.n_type, - (char *) data->d_buf + name_offset, offset); - } - } - } - - return last_offset; -} - - -static void -check_note (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Phdr *phdr, int cnt) -{ - if (ehdr->e_type != ET_CORE && ehdr->e_type != ET_REL - && ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) - ERROR (gettext ("\ -phdr[%d]: no note entries defined for the type of file\n"), - cnt); - - if (is_debuginfo) - /* The p_offset values in a separate debug file are bogus. */ - return; - - if (phdr->p_filesz == 0) - return; - - GElf_Off notes_size = 0; - Elf_Data *data = elf_getdata_rawchunk (ebl->elf, - phdr->p_offset, phdr->p_filesz, - (phdr->p_align == 8 - ? ELF_T_NHDR8 : ELF_T_NHDR)); - if (data != NULL && data->d_buf != NULL) - notes_size = check_note_data (ebl, ehdr, data, 0, cnt, phdr->p_offset); - - if (notes_size == 0) - ERROR (gettext ("phdr[%d]: cannot get content of note section: %s\n"), - cnt, elf_errmsg (-1)); - else if (notes_size != phdr->p_filesz) - ERROR (gettext ("phdr[%d]: extra %" PRIu64 " bytes after last note\n"), - cnt, phdr->p_filesz - notes_size); -} - - -static void -check_note_section (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) -{ - if (shdr->sh_size == 0) - return; - - Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL); - if (data == NULL || data->d_buf == NULL) - { - ERROR (gettext ("section [%2d] '%s': cannot get section data\n"), - idx, section_name (ebl, idx)); - return; - } - - if (ehdr->e_type != ET_CORE && ehdr->e_type != ET_REL - && ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) - ERROR (gettext ("\ -section [%2d] '%s': no note entries defined for the type of file\n"), - idx, section_name (ebl, idx)); - - GElf_Off notes_size = check_note_data (ebl, ehdr, data, idx, 0, 0); - - if (notes_size == 0) - ERROR (gettext ("section [%2d] '%s': cannot get content of note section\n"), - idx, section_name (ebl, idx)); - else if (notes_size != shdr->sh_size) - ERROR (gettext ("section [%2d] '%s': extra %" PRIu64 - " bytes after last note\n"), - idx, section_name (ebl, idx), shdr->sh_size - notes_size); -} - - -/* Index of the PT_GNU_EH_FRAME program eader entry. */ -static int pt_gnu_eh_frame_pndx; - - -static void -check_program_header (Ebl *ebl, GElf_Ehdr *ehdr) -{ - if (ehdr->e_phoff == 0) - return; - - if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN - && ehdr->e_type != ET_CORE) - ERROR (gettext ("\ -only executables, shared objects, and core files can have program headers\n")); - - int num_pt_interp = 0; - int num_pt_tls = 0; - int num_pt_relro = 0; - - for (unsigned int cnt = 0; cnt < phnum; ++cnt) - { - GElf_Phdr phdr_mem; - GElf_Phdr *phdr; - - phdr = gelf_getphdr (ebl->elf, cnt, &phdr_mem); - if (phdr == NULL) - { - ERROR (gettext ("cannot get program header entry %d: %s\n"), - cnt, elf_errmsg (-1)); - continue; - } - - if (phdr->p_type >= PT_NUM && phdr->p_type != PT_GNU_EH_FRAME - && phdr->p_type != PT_GNU_STACK && phdr->p_type != PT_GNU_RELRO - /* Check for a known machine-specific type. */ - && ebl_segment_type_name (ebl, phdr->p_type, NULL, 0) == NULL) - ERROR (gettext ("\ -program header entry %d: unknown program header entry type %#" PRIx64 "\n"), - cnt, (uint64_t) phdr->p_type); - - if (phdr->p_type == PT_LOAD) - has_loadable_segment = true; - else if (phdr->p_type == PT_INTERP) - { - if (++num_pt_interp != 1) - { - if (num_pt_interp == 2) - ERROR (gettext ("\ -more than one INTERP entry in program header\n")); - } - has_interp_segment = true; - } - else if (phdr->p_type == PT_TLS) - { - if (++num_pt_tls == 2) - ERROR (gettext ("more than one TLS entry in program header\n")); - } - else if (phdr->p_type == PT_NOTE) - check_note (ebl, ehdr, phdr, cnt); - else if (phdr->p_type == PT_DYNAMIC) - { - if (ehdr->e_type == ET_EXEC && ! has_interp_segment) - ERROR (gettext ("\ -static executable cannot have dynamic sections\n")); - else - { - /* Check that the .dynamic section, if it exists, has - the same address. */ - Elf_Scn *scn = NULL; - while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) - { - GElf_Shdr shdr_mem; - GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); - if (shdr != NULL && shdr->sh_type == SHT_DYNAMIC) - { - if (phdr->p_offset != shdr->sh_offset) - ERROR (gettext ("\ -dynamic section reference in program header has wrong offset\n")); - if (phdr->p_memsz != shdr->sh_size) - ERROR (gettext ("\ -dynamic section size mismatch in program and section header\n")); - break; - } - } - } - } - else if (phdr->p_type == PT_GNU_RELRO) - { - if (++num_pt_relro == 2) - ERROR (gettext ("\ -more than one GNU_RELRO entry in program header\n")); - else - { - /* Check that the region is in a writable segment. */ - unsigned int inner; - for (inner = 0; inner < phnum; ++inner) - { - GElf_Phdr phdr2_mem; - GElf_Phdr *phdr2; - - phdr2 = gelf_getphdr (ebl->elf, inner, &phdr2_mem); - if (phdr2 == NULL) - continue; - - if (phdr2->p_type == PT_LOAD - && phdr->p_vaddr >= phdr2->p_vaddr - && (phdr->p_vaddr + phdr->p_memsz - <= phdr2->p_vaddr + phdr2->p_memsz)) - { - if ((phdr2->p_flags & PF_W) == 0) - ERROR (gettext ("\ -loadable segment GNU_RELRO applies to is not writable\n")); - /* Unless fully covered, relro flags could be a - subset of the phdrs2 flags. For example the load - segment could also have PF_X set. */ - if (phdr->p_vaddr == phdr2->p_vaddr - && (phdr->p_vaddr + phdr->p_memsz - == phdr2->p_vaddr + phdr2->p_memsz)) - { - if ((phdr2->p_flags & ~PF_W) - != (phdr->p_flags & ~PF_W)) - ERROR (gettext ("\ -loadable segment [%u] flags do not match GNU_RELRO [%u] flags\n"), - cnt, inner); - } - else - { - if ((phdr->p_flags & ~phdr2->p_flags) != 0) - ERROR (gettext ("\ -GNU_RELRO [%u] flags are not a subset of the loadable segment [%u] flags\n"), - inner, cnt); - } - break; - } - } - - if (inner >= phnum) - ERROR (gettext ("\ -%s segment not contained in a loaded segment\n"), "GNU_RELRO"); - } - } - else if (phdr->p_type == PT_PHDR) - { - /* Check that the region is in a writable segment. */ - unsigned int inner; - for (inner = 0; inner < phnum; ++inner) - { - GElf_Phdr phdr2_mem; - GElf_Phdr *phdr2; - - phdr2 = gelf_getphdr (ebl->elf, inner, &phdr2_mem); - if (phdr2 != NULL - && phdr2->p_type == PT_LOAD - && phdr->p_vaddr >= phdr2->p_vaddr - && (phdr->p_vaddr + phdr->p_memsz - <= phdr2->p_vaddr + phdr2->p_memsz)) - break; - } - - if (inner >= phnum) - ERROR (gettext ("\ -%s segment not contained in a loaded segment\n"), "PHDR"); - - /* Check that offset in segment corresponds to offset in ELF - header. */ - if (phdr->p_offset != ehdr->e_phoff) - ERROR (gettext ("\ -program header offset in ELF header and PHDR entry do not match")); - } - else if (phdr->p_type == PT_GNU_EH_FRAME) - { - /* If there is an .eh_frame_hdr section it must be - referenced by this program header entry. */ - Elf_Scn *scn = NULL; - GElf_Shdr shdr_mem; - GElf_Shdr *shdr = NULL; - bool any = false; - while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) - { - any = true; - shdr = gelf_getshdr (scn, &shdr_mem); - if (shdr != NULL - && ((is_debuginfo && shdr->sh_type == SHT_NOBITS) - || (! is_debuginfo - && (shdr->sh_type == SHT_PROGBITS - || shdr->sh_type == SHT_X86_64_UNWIND))) - && elf_strptr (ebl->elf, shstrndx, shdr->sh_name) != NULL - && ! strcmp (".eh_frame_hdr", - elf_strptr (ebl->elf, shstrndx, shdr->sh_name))) - { - if (! is_debuginfo) - { - if (phdr->p_offset != shdr->sh_offset) - ERROR (gettext ("\ -call frame search table reference in program header has wrong offset\n")); - if (phdr->p_memsz != shdr->sh_size) - ERROR (gettext ("\ -call frame search table size mismatch in program and section header\n")); - } - break; - } - } - - if (scn == NULL) - { - /* If there is no section header table we don't - complain. But if there is one there should be an - entry for .eh_frame_hdr. */ - if (any) - ERROR (gettext ("\ -PT_GNU_EH_FRAME present but no .eh_frame_hdr section\n")); - } - else - { - /* The section must be allocated and not be writable and - executable. */ - if ((phdr->p_flags & PF_R) == 0) - ERROR (gettext ("\ -call frame search table must be allocated\n")); - else if (shdr != NULL && (shdr->sh_flags & SHF_ALLOC) == 0) - ERROR (gettext ("\ -section [%2zu] '%s' must be allocated\n"), elf_ndxscn (scn), ".eh_frame_hdr"); - - if ((phdr->p_flags & PF_W) != 0) - ERROR (gettext ("\ -call frame search table must not be writable\n")); - else if (shdr != NULL && (shdr->sh_flags & SHF_WRITE) != 0) - ERROR (gettext ("\ -section [%2zu] '%s' must not be writable\n"), - elf_ndxscn (scn), ".eh_frame_hdr"); - - if ((phdr->p_flags & PF_X) != 0) - ERROR (gettext ("\ -call frame search table must not be executable\n")); - else if (shdr != NULL && (shdr->sh_flags & SHF_EXECINSTR) != 0) - ERROR (gettext ("\ -section [%2zu] '%s' must not be executable\n"), - elf_ndxscn (scn), ".eh_frame_hdr"); - } - - /* Remember which entry this is. */ - pt_gnu_eh_frame_pndx = cnt; - } - - if (phdr->p_filesz > phdr->p_memsz - && (phdr->p_memsz != 0 || phdr->p_type != PT_NOTE)) - ERROR (gettext ("\ -program header entry %d: file size greater than memory size\n"), - cnt); - - if (phdr->p_align > 1) - { - if (!powerof2 (phdr->p_align)) - ERROR (gettext ("\ -program header entry %d: alignment not a power of 2\n"), cnt); - else if ((phdr->p_vaddr - phdr->p_offset) % phdr->p_align != 0) - ERROR (gettext ("\ -program header entry %d: file offset and virtual address not module of alignment\n"), cnt); - } - } -} - - -static void -check_exception_data (Ebl *ebl __attribute__ ((unused)), - GElf_Ehdr *ehdr __attribute__ ((unused))) -{ - if ((ehdr->e_type == ET_EXEC || ehdr->e_type == ET_DYN) - && pt_gnu_eh_frame_pndx == 0 && eh_frame_hdr_scnndx != 0) - ERROR (gettext ("executable/DSO with .eh_frame_hdr section does not have " - "a PT_GNU_EH_FRAME program header entry")); -} - - -/* Process one file. */ -static void -process_elf_file (Elf *elf, const char *prefix, const char *suffix, - const char *fname, size_t size, bool only_one) -{ - /* Reset variables. */ - ndynamic = 0; - nverneed = 0; - nverdef = 0; - textrel = false; - needed_textrel = false; - has_loadable_segment = false; - has_interp_segment = false; - - GElf_Ehdr ehdr_mem; - GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem); - Ebl *ebl; - - /* Print the file name. */ - if (!only_one) - { - if (prefix != NULL) - printf ("\n%s(%s)%s:\n", prefix, fname, suffix); - else - printf ("\n%s:\n", fname); - } - - if (ehdr == NULL) - { - ERROR (gettext ("cannot read ELF header: %s\n"), elf_errmsg (-1)); - return; - } - - ebl = ebl_openbackend (elf); - /* If there is no appropriate backend library we cannot test - architecture and OS specific features. Any encountered extension - is an error. */ - - /* Go straight by the gABI, check all the parts in turn. */ - check_elf_header (ebl, ehdr, size); - - /* Check the program header. */ - check_program_header (ebl, ehdr); - - /* Next the section headers. It is OK if there are no section - headers at all. */ - check_sections (ebl, ehdr); - - /* Check the exception handling data, if it exists. */ - if (pt_gnu_eh_frame_pndx != 0 || eh_frame_hdr_scnndx != 0 - || eh_frame_scnndx != 0 || gcc_except_table_scnndx != 0) - check_exception_data (ebl, ehdr); - - /* Report if no relocation section needed the text relocation flag. */ - if (textrel && !needed_textrel) - ERROR (gettext ("text relocation flag set but not needed\n")); - - /* Free the resources. */ - ebl_closebackend (ebl); -} - - -#include "debugpred.h" diff --git a/tests/asm-tst1.c b/tests/asm-tst1.c index cdf2a92..9afc676 100644 --- a/tests/asm-tst1.c +++ b/tests/asm-tst1.c @@ -20,7 +20,6 @@ #endif #include -#include ELFUTILS_HEADER(ebl) #include ELFUTILS_HEADER(asm) #include #include diff --git a/tests/asm-tst1.c.libasm-ebl b/tests/asm-tst1.c.libasm-ebl deleted file mode 100644 index 9afc676..0000000 --- a/tests/asm-tst1.c.libasm-ebl +++ /dev/null @@ -1,256 +0,0 @@ -/* Copyright (C) 2002, 2005 Red Hat, Inc. - This file is part of elfutils. - Written by Ulrich Drepper , 2002. - - This file is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - elfutils is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include -#include ELFUTILS_HEADER(asm) -#include -#include -#include -#include - - -static const char fname[] = "asm-tst1-out.o"; - - -static const GElf_Ehdr expected_ehdr = - { - .e_ident = { [EI_MAG0] = ELFMAG0, - [EI_MAG1] = ELFMAG1, - [EI_MAG2] = ELFMAG2, - [EI_MAG3] = ELFMAG3, - [EI_CLASS] = ELFCLASS32, - [EI_DATA] = ELFDATA2LSB, - [EI_VERSION] = EV_CURRENT }, - .e_type = ET_REL, - .e_machine = EM_386, - .e_version = EV_CURRENT, - .e_shoff = 88, - .e_ehsize = sizeof (Elf32_Ehdr), - .e_shentsize = sizeof (Elf32_Shdr), - .e_shnum = 4, - .e_shstrndx = 3 - }; - - -static const char *scnnames[4] = - { - [0] = "", - [1] = ".text", - [2] = ".data", - [3] = ".shstrtab" - }; - - -int -main (void) -{ - AsmCtx_t *ctx; - AsmScn_t *scn1; - AsmScn_t *scn2; - int fd; - Elf *elf; - GElf_Ehdr ehdr_mem; - GElf_Ehdr *ehdr; - int result = 0; - size_t cnt; - - elf_version (EV_CURRENT); - - Ebl *ebl = ebl_openbackend_machine (EM_386); - if (ebl == NULL) - { - puts ("cannot open backend library"); - return 1; - } - - ctx = asm_begin (fname, ebl, false); - if (ctx == NULL) - { - printf ("cannot create assembler context: %s\n", asm_errmsg (-1)); - return 1; - } - - /* Create two sections. */ - scn1 = asm_newscn (ctx, ".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR); - scn2 = asm_newscn (ctx, ".data", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE); - if (scn1 == NULL || scn2 == NULL) - { - printf ("cannot create section in output file: %s\n", asm_errmsg (-1)); - asm_abort (ctx); - return 1; - } - - /* Special alignment for the .text section. */ - if (asm_align (scn1, 32) != 0) - { - printf ("cannot align .text section: %s\n", asm_errmsg (-1)); - result = 1; - } - - /* Create the output file. */ - if (asm_end (ctx) != 0) - { - printf ("cannot create output file: %s\n", asm_errmsg (-1)); - asm_abort (ctx); - return 1; - } - - /* Check the file. */ - fd = open (fname, O_RDONLY); - if (fd == -1) - { - printf ("cannot open generated file: %m\n"); - result = 1; - goto out; - } - - elf = elf_begin (fd, ELF_C_READ, NULL); - if (elf == NULL) - { - printf ("cannot create ELF descriptor: %s\n", elf_errmsg (-1)); - result = 1; - goto out_close; - } - if (elf_kind (elf) != ELF_K_ELF) - { - puts ("not a valid ELF file"); - result = 1; - goto out_close2; - } - - ehdr = gelf_getehdr (elf, &ehdr_mem); - if (ehdr == NULL) - { - printf ("cannot get ELF header: %s\n", elf_errmsg (-1)); - result = 1; - goto out_close2; - } - - if (memcmp (ehdr, &expected_ehdr, sizeof (GElf_Ehdr)) != 0) - { - puts ("ELF header does not match"); - result = 1; - goto out_close2; - } - - for (cnt = 1; cnt < 4; ++cnt) - { - Elf_Scn *scn; - GElf_Shdr shdr_mem; - GElf_Shdr *shdr; - - scn = elf_getscn (elf, cnt); - if (scn == NULL) - { - printf ("cannot get section %zd: %s\n", cnt, elf_errmsg (-1)); - result = 1; - continue; - } - - shdr = gelf_getshdr (scn, &shdr_mem); - if (shdr == NULL) - { - printf ("cannot get section header for section %zd: %s\n", - cnt, elf_errmsg (-1)); - result = 1; - continue; - } - - if (strcmp (elf_strptr (elf, ehdr->e_shstrndx, shdr->sh_name), - scnnames[cnt]) != 0) - { - printf ("section %zd's name differs: %s vs %s\n", cnt, - elf_strptr (elf, ehdr->e_shstrndx, shdr->sh_name), - scnnames[cnt]); - result = 1; - } - - if (shdr->sh_type != (cnt == 3 ? SHT_STRTAB : SHT_PROGBITS)) - { - printf ("section %zd's type differs\n", cnt); - result = 1; - } - - if ((cnt == 1 && shdr->sh_flags != (SHF_ALLOC | SHF_EXECINSTR)) - || (cnt == 2 && shdr->sh_flags != (SHF_ALLOC | SHF_WRITE)) - || (cnt == 3 && shdr->sh_flags != 0)) - { - printf ("section %zd's flags differs\n", cnt); - result = 1; - } - - if (shdr->sh_addr != 0) - { - printf ("section %zd's address differs\n", cnt); - result = 1; - } - - if (shdr->sh_offset != ((sizeof (Elf32_Ehdr) + 31) & ~31)) - { - printf ("section %zd's offset differs\n", cnt); - result = 1; - } - - if ((cnt != 3 && shdr->sh_size != 0) - || (cnt == 3 && shdr->sh_size != 23)) - { - printf ("section %zd's size differs\n", cnt); - result = 1; - } - - if (shdr->sh_link != 0) - { - printf ("section %zd's link differs\n", cnt); - result = 1; - } - - if (shdr->sh_info != 0) - { - printf ("section %zd's info differs\n", cnt); - result = 1; - } - - if ((cnt == 1 && shdr->sh_addralign != 32) - || (cnt != 1 && shdr->sh_addralign != 1)) - { - printf ("section %zd's addralign differs\n", cnt); - result = 1; - } - - if (shdr->sh_entsize != 0) - { - printf ("section %zd's entsize differs\n", cnt); - result = 1; - } - } - - out_close2: - elf_end (elf); - out_close: - close (fd); - out: - /* We don't need the file anymore. */ - unlink (fname); - - ebl_closebackend (ebl); - - return result; -} diff --git a/tests/asm-tst2.c b/tests/asm-tst2.c index 9e88b70..2556d0c 100644 --- a/tests/asm-tst2.c +++ b/tests/asm-tst2.c @@ -20,7 +20,6 @@ #endif #include -#include ELFUTILS_HEADER(ebl) #include ELFUTILS_HEADER(asm) #include #include diff --git a/tests/asm-tst2.c.libasm-ebl b/tests/asm-tst2.c.libasm-ebl deleted file mode 100644 index 2556d0c..0000000 --- a/tests/asm-tst2.c.libasm-ebl +++ /dev/null @@ -1,278 +0,0 @@ -/* Copyright (C) 2002, 2005 Red Hat, Inc. - This file is part of elfutils. - Written by Ulrich Drepper , 2002. - - This file is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - elfutils is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include -#include ELFUTILS_HEADER(asm) -#include -#include -#include -#include - - -static const char fname[] = "asm-tst2-out.o"; - - -static const GElf_Ehdr expected_ehdr = - { - .e_ident = { [EI_MAG0] = ELFMAG0, - [EI_MAG1] = ELFMAG1, - [EI_MAG2] = ELFMAG2, - [EI_MAG3] = ELFMAG3, - [EI_CLASS] = ELFCLASS32, - [EI_DATA] = ELFDATA2LSB, - [EI_VERSION] = EV_CURRENT }, - .e_type = ET_REL, - .e_machine = EM_386, - .e_version = EV_CURRENT, - .e_shoff = 96, - .e_ehsize = sizeof (Elf32_Ehdr), - .e_shentsize = sizeof (Elf32_Shdr), - .e_shnum = 3, - .e_shstrndx = 2 - }; - - -static const char *scnnames[3] = - { - [0] = "", - [1] = ".data", - [2] = ".shstrtab" - }; - - -int -main (void) -{ - AsmCtx_t *ctx; - AsmScn_t *scn1; - AsmScn_t *scn2; - int result = 0; - int fd; - Elf *elf; - GElf_Ehdr ehdr_mem; - GElf_Ehdr *ehdr; - size_t cnt; - - elf_version (EV_CURRENT); - - Ebl *ebl = ebl_openbackend_machine (EM_386); - if (ebl == NULL) - { - puts ("cannot open backend library"); - return 1; - } - - ctx = asm_begin (fname, ebl, false); - if (ctx == NULL) - { - printf ("cannot create assembler context: %s\n", asm_errmsg (-1)); - return 1; - } - - /* Create two sections. */ - scn1 = asm_newscn (ctx, ".data", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE); - scn2 = asm_newsubscn (scn1, 1); - if (scn1 == NULL || scn2 == NULL) - { - printf ("cannot create section in output file: %s\n", asm_errmsg (-1)); - asm_abort (ctx); - return 1; - } - - /* Special alignment for the .text section. */ - if (asm_align (scn1, 16) != 0) - { - printf ("cannot align .text section: %s\n", asm_errmsg (-1)); - result = 1; - } - - /* Add a few strings. */ - if (asm_addstrz (scn1, "one", 4) != 0) - { - printf ("cannot insert first string: %s\n", asm_errmsg (-1)); - result = 1; - } - if (asm_addstrz (scn2, "three", 0) != 0) - { - printf ("cannot insert second string: %s\n", asm_errmsg (-1)); - result = 1; - } - if (asm_addstrz (scn1, "two", 4) != 0) - { - printf ("cannot insert third string: %s\n", asm_errmsg (-1)); - result = 1; - } - - /* Create the output file. */ - if (asm_end (ctx) != 0) - { - printf ("cannot create output file: %s\n", asm_errmsg (-1)); - asm_abort (ctx); - return 1; - } - - /* Check the file. */ - fd = open (fname, O_RDONLY); - if (fd == -1) - { - printf ("cannot open generated file: %m\n"); - result = 1; - goto out; - } - - elf = elf_begin (fd, ELF_C_READ, NULL); - if (elf == NULL) - { - printf ("cannot create ELF descriptor: %s\n", elf_errmsg (-1)); - result = 1; - goto out_close; - } - if (elf_kind (elf) != ELF_K_ELF) - { - puts ("not a valid ELF file"); - result = 1; - goto out_close2; - } - - ehdr = gelf_getehdr (elf, &ehdr_mem); - if (ehdr == NULL) - { - printf ("cannot get ELF header: %s\n", elf_errmsg (-1)); - result = 1; - goto out_close2; - } - - if (memcmp (ehdr, &expected_ehdr, sizeof (GElf_Ehdr)) != 0) - { - puts ("ELF header does not match"); - result = 1; - goto out_close2; - } - - for (cnt = 1; cnt < 3; ++cnt) - { - Elf_Scn *scn; - GElf_Shdr shdr_mem; - GElf_Shdr *shdr; - - scn = elf_getscn (elf, cnt); - if (scn == NULL) - { - printf ("cannot get section %zd: %s\n", cnt, elf_errmsg (-1)); - result = 1; - continue; - } - - shdr = gelf_getshdr (scn, &shdr_mem); - if (shdr == NULL) - { - printf ("cannot get section header for section %zd: %s\n", - cnt, elf_errmsg (-1)); - result = 1; - continue; - } - - if (strcmp (elf_strptr (elf, ehdr->e_shstrndx, shdr->sh_name), - scnnames[cnt]) != 0) - { - printf ("section %zd's name differs: %s vs %s\n", cnt, - elf_strptr (elf, ehdr->e_shstrndx, shdr->sh_name), - scnnames[cnt]); - result = 1; - } - - if (shdr->sh_type != (cnt == 2 ? SHT_STRTAB : SHT_PROGBITS)) - { - printf ("section %zd's type differs\n", cnt); - result = 1; - } - - if ((cnt == 1 && shdr->sh_flags != (SHF_ALLOC | SHF_WRITE)) - || (cnt == 2 && shdr->sh_flags != 0)) - { - printf ("section %zd's flags differs\n", cnt); - result = 1; - } - - if (shdr->sh_addr != 0) - { - printf ("section %zd's address differs\n", cnt); - result = 1; - } - - if ((cnt == 1 && shdr->sh_offset != ((sizeof (Elf32_Ehdr) + 15) & ~15)) - || (cnt == 2 - && shdr->sh_offset != (((sizeof (Elf32_Ehdr) + 15) & ~15) - + strlen ("one") + 1 - + strlen ("two") + 1 - + strlen ("three") + 1))) - { - printf ("section %zd's offset differs\n", cnt); - result = 1; - } - - if ((cnt == 1 && shdr->sh_size != (strlen ("one") + 1 - + strlen ("two") + 1 - + strlen ("three") + 1)) - || (cnt == 2 && shdr->sh_size != 17)) - { - printf ("section %zd's size differs\n", cnt); - result = 1; - } - - if (shdr->sh_link != 0) - { - printf ("section %zd's link differs\n", cnt); - result = 1; - } - - if (shdr->sh_info != 0) - { - printf ("section %zd's info differs\n", cnt); - result = 1; - } - - if ((cnt == 1 && shdr->sh_addralign != 16) - || (cnt != 1 && shdr->sh_addralign != 1)) - { - printf ("section %zd's addralign differs\n", cnt); - result = 1; - } - - if (shdr->sh_entsize != 0) - { - printf ("section %zd's entsize differs\n", cnt); - result = 1; - } - } - - out_close2: - elf_end (elf); - out_close: - close (fd); - out: - /* We don't need the file anymore. */ - unlink (fname); - - ebl_closebackend (ebl); - - return result; -} diff --git a/tests/asm-tst3.c b/tests/asm-tst3.c index 39c1d90..e52cfbe 100644 --- a/tests/asm-tst3.c +++ b/tests/asm-tst3.c @@ -20,7 +20,6 @@ #endif #include -#include ELFUTILS_HEADER(ebl) #include ELFUTILS_HEADER(asm) #include #include diff --git a/tests/asm-tst3.c.libasm-ebl b/tests/asm-tst3.c.libasm-ebl deleted file mode 100644 index e52cfbe..0000000 --- a/tests/asm-tst3.c.libasm-ebl +++ /dev/null @@ -1,339 +0,0 @@ -/* Copyright (C) 2002, 2005 Red Hat, Inc. - This file is part of elfutils. - Written by Ulrich Drepper , 2002. - - This file is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - elfutils is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include -#include ELFUTILS_HEADER(asm) -#include -#include -#include -#include - - -static const char fname[] = "asm-tst3-out.o"; - - -static const char *scnnames[5] = - { - [0] = "", - [1] = ".data", - [2] = ".strtab", - [3] = ".symtab", - [4] = ".shstrtab" - }; - - -static unsigned int scntypes[5] = - { - [0] = SHT_NULL, - [1] = SHT_PROGBITS, - [2] = SHT_STRTAB, - [3] = SHT_SYMTAB, - [4] = SHT_STRTAB - }; - - -int -main (void) -{ - AsmCtx_t *ctx; - AsmScn_t *scn1; - AsmScn_t *scn2; - int result = 0; - int fd; - Elf *elf; - GElf_Ehdr ehdr_mem; - GElf_Ehdr *ehdr; - size_t cnt; - - elf_version (EV_CURRENT); - - Ebl *ebl = ebl_openbackend_machine (EM_386); - if (ebl == NULL) - { - puts ("cannot open backend library"); - return 1; - } - - ctx = asm_begin (fname, ebl, false); - if (ctx == NULL) - { - printf ("cannot create assembler context: %s\n", asm_errmsg (-1)); - return 1; - } - - /* Create two sections. */ - scn1 = asm_newscn (ctx, ".data", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE); - scn2 = asm_newsubscn (scn1, 1); - if (scn1 == NULL || scn2 == NULL) - { - printf ("cannot create section in output file: %s\n", asm_errmsg (-1)); - asm_abort (ctx); - return 1; - } - - /* Special alignment for the .text section. */ - if (asm_align (scn1, 16) != 0) - { - printf ("cannot align .text section: %s\n", asm_errmsg (-1)); - result = 1; - } - - /* Add a few strings with names. */ - if (asm_newsym (scn1, "one", 4, STT_OBJECT, STB_GLOBAL) == NULL) - { - printf ("cannot create first name: %s\n", asm_errmsg (-1)); - result = 1; - } - if (asm_addstrz (scn1, "one", 4) != 0) - { - printf ("cannot insert first string: %s\n", asm_errmsg (-1)); - result = 1; - } - if (asm_newsym (scn2, "three", 6, STT_OBJECT, STB_WEAK) == NULL) - { - printf ("cannot create second name: %s\n", asm_errmsg (-1)); - result = 1; - } - if (asm_addstrz (scn2, "three", 0) != 0) - { - printf ("cannot insert second string: %s\n", asm_errmsg (-1)); - result = 1; - } - if (asm_newsym (scn1, "two", 4, STT_OBJECT, STB_LOCAL) == NULL) - { - printf ("cannot create third name: %s\n", asm_errmsg (-1)); - result = 1; - } - if (asm_addstrz (scn1, "two", 4) != 0) - { - printf ("cannot insert third string: %s\n", asm_errmsg (-1)); - result = 1; - } - - /* Create the output file. */ - if (asm_end (ctx) != 0) - { - printf ("cannot create output file: %s\n", asm_errmsg (-1)); - asm_abort (ctx); - return 1; - } - - /* Check the file. */ - fd = open (fname, O_RDONLY); - if (fd == -1) - { - printf ("cannot open generated file: %m\n"); - result = 1; - goto out; - } - - elf = elf_begin (fd, ELF_C_READ, NULL); - if (elf == NULL) - { - printf ("cannot create ELF descriptor: %s\n", elf_errmsg (-1)); - result = 1; - goto out_close; - } - if (elf_kind (elf) != ELF_K_ELF) - { - puts ("not a valid ELF file"); - result = 1; - goto out_close2; - } - - ehdr = gelf_getehdr (elf, &ehdr_mem); - if (ehdr == NULL) - { - printf ("cannot get ELF header: %s\n", elf_errmsg (-1)); - result = 1; - goto out_close2; - } - - for (cnt = 1; cnt < 5; ++cnt) - { - Elf_Scn *scn; - GElf_Shdr shdr_mem; - GElf_Shdr *shdr; - - scn = elf_getscn (elf, cnt); - if (scn == NULL) - { - printf ("cannot get section %zd: %s\n", cnt, elf_errmsg (-1)); - result = 1; - continue; - } - - shdr = gelf_getshdr (scn, &shdr_mem); - if (shdr == NULL) - { - printf ("cannot get section header for section %zd: %s\n", - cnt, elf_errmsg (-1)); - result = 1; - continue; - } - - if (strcmp (elf_strptr (elf, ehdr->e_shstrndx, shdr->sh_name), - scnnames[cnt]) != 0) - { - printf ("section %zd's name differs: %s vs %s\n", cnt, - elf_strptr (elf, ehdr->e_shstrndx, shdr->sh_name), - scnnames[cnt]); - result = 1; - } - - if (shdr->sh_type != scntypes[cnt]) - { - printf ("section %zd's type differs\n", cnt); - result = 1; - } - - if ((cnt == 1 && shdr->sh_flags != (SHF_ALLOC | SHF_WRITE)) - || (cnt != 1 && shdr->sh_flags != 0)) - { - printf ("section %zd's flags differs\n", cnt); - result = 1; - } - - if (shdr->sh_addr != 0) - { - printf ("section %zd's address differs\n", cnt); - result = 1; - } - - if (cnt == 3) - { - Elf_Data *data; - - if (shdr->sh_link != 2) - { - puts ("symbol table has incorrect link"); - result = 1; - } - - data = elf_getdata (scn, NULL); - if (data == NULL) - { - puts ("cannot get data of symbol table"); - result = 1; - } - else - { - size_t inner; - - for (inner = 1; - inner < (shdr->sh_size - / gelf_fsize (elf, ELF_T_SYM, 1, EV_CURRENT)); - ++inner) - { - GElf_Sym sym_mem; - GElf_Sym *sym; - - sym = gelf_getsym (data, inner, &sym_mem); - if (sym == NULL) - { - printf ("cannot get symbol %zu: %s\n", - inner, elf_errmsg (-1)); - result = 1; - } - else - { - /* The order of the third and fourth entry depends - on how the hash table is organized. */ - static const char *names[4] = - { - [0] = "", - [1] = "two", - [2] = "one", - [3] = "three" - }; - static const int info[4] = - { - [0] = GELF_ST_INFO (STB_LOCAL, STT_NOTYPE), - [1] = GELF_ST_INFO (STB_LOCAL, STT_OBJECT), - [2] = GELF_ST_INFO (STB_GLOBAL, STT_OBJECT), - [3] = GELF_ST_INFO (STB_WEAK, STT_OBJECT) - }; - static const unsigned value[4] = - { - [0] = 0, - [1] = 4, - [2] = 0, - [3] = 8 - }; - - if (strcmp (names[inner], - elf_strptr (elf, shdr->sh_link, - sym->st_name)) != 0) - { - printf ("symbol %zu has different name\n", inner); - result = 1; - } - - if (sym->st_value != value[inner]) - { - printf ("symbol %zu has wrong value\n", inner); - result = 1; - } - - if (sym->st_other != 0) - { - printf ("symbol %zu has wrong other info\n", inner); - result = 1; - } - - if (sym->st_shndx != 1) - { - printf ("symbol %zu has wrong section reference\n", - inner); - result = 1; - } - - if (sym->st_info != info[inner]) - { - printf ("symbol %zu has wrong type or binding\n", - inner); - result = 1; - } - - if ((inner != 3 && sym->st_size != 4) - || (inner == 3 && sym->st_size != 6)) - { - printf ("symbol %zu has wrong size\n", inner); - result = 1; - } - } - } - } - } - } - - out_close2: - elf_end (elf); - out_close: - close (fd); - out: - /* We don't need the file anymore. */ - unlink (fname); - - ebl_closebackend (ebl); - - return result; -} diff --git a/tests/asm-tst4.c b/tests/asm-tst4.c index 5114938..52e9e20 100644 --- a/tests/asm-tst4.c +++ b/tests/asm-tst4.c @@ -20,7 +20,6 @@ #endif #include -#include ELFUTILS_HEADER(ebl) #include ELFUTILS_HEADER(asm) #include #include diff --git a/tests/asm-tst4.c.libasm-ebl b/tests/asm-tst4.c.libasm-ebl deleted file mode 100644 index 52e9e20..0000000 --- a/tests/asm-tst4.c.libasm-ebl +++ /dev/null @@ -1,104 +0,0 @@ -/* Copyright (C) 2002-2012 Red Hat, Inc. - This file is part of elfutils. - Written by Ulrich Drepper , 2002. - - This file is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - elfutils is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include -#include ELFUTILS_HEADER(asm) -#include -#include -#include -#include -#include -#include - - -static const char fname[] = "asm-tst4-out.o"; - - -int -main (void) -{ - AsmCtx_t *ctx; - int result = 0; - size_t cnt; - - elf_version (EV_CURRENT); - - Ebl *ebl = ebl_openbackend_machine (EM_386); - if (ebl == NULL) - { - puts ("cannot open backend library"); - return 1; - } - - ctx = asm_begin (fname, ebl, false); - if (ctx == NULL) - { - printf ("cannot create assembler context: %s\n", asm_errmsg (-1)); - return 1; - } - - /* Create 66000 sections. */ - for (cnt = 0; cnt < 66000; ++cnt) - { - char buf[20]; - AsmScn_t *scn; - - /* Create a unique name. */ - snprintf (buf, sizeof (buf), ".data.%zu", cnt); - - /* Create the section. */ - scn = asm_newscn (ctx, buf, SHT_PROGBITS, SHF_ALLOC | SHF_WRITE); - if (scn == NULL) - { - printf ("cannot create section \"%s\" in output file: %s\n", - buf, asm_errmsg (-1)); - asm_abort (ctx); - return 1; - } - - /* Add some content. */ - if (asm_adduint32 (scn, cnt) != 0) - { - printf ("cannot create content of section \"%s\": %s\n", - buf, asm_errmsg (-1)); - asm_abort (ctx); - return 1; - } - } - - /* Create the output file. */ - if (asm_end (ctx) != 0) - { - printf ("cannot create output file: %s\n", asm_errmsg (-1)); - asm_abort (ctx); - return 1; - } - - if (result == 0) - result = WEXITSTATUS (system ("../src/elflint -q asm-tst4-out.o")); - - /* We don't need the file anymore. */ - unlink (fname); - - ebl_closebackend (ebl); - - return result; -} diff --git a/tests/asm-tst5.c b/tests/asm-tst5.c index dcb852f..5a29b01 100644 --- a/tests/asm-tst5.c +++ b/tests/asm-tst5.c @@ -20,7 +20,6 @@ #endif #include -#include ELFUTILS_HEADER(ebl) #include ELFUTILS_HEADER(asm) #include #include diff --git a/tests/asm-tst5.c.libasm-ebl b/tests/asm-tst5.c.libasm-ebl deleted file mode 100644 index 5a29b01..0000000 --- a/tests/asm-tst5.c.libasm-ebl +++ /dev/null @@ -1,116 +0,0 @@ -/* Copyright (C) 2002-2012 Red Hat, Inc. - This file is part of elfutils. - Written by Ulrich Drepper , 2002. - - This file is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - elfutils is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include -#include ELFUTILS_HEADER(asm) -#include -#include -#include -#include -#include -#include - -#include "system.h" - - -static const char fname[] = "asm-tst5-out.o"; - - -int -main (void) -{ - AsmCtx_t *ctx; - int result = 0; - size_t cnt; - - elf_version (EV_CURRENT); - - Ebl *ebl = ebl_openbackend_machine (EM_386); - if (ebl == NULL) - { - puts ("cannot open backend library"); - return 1; - } - - ctx = asm_begin (fname, ebl, false); - if (ctx == NULL) - { - printf ("cannot create assembler context: %s\n", asm_errmsg (-1)); - return 1; - } - - /* Create 66000 sections. */ - for (cnt = 0; cnt < 66000; ++cnt) - { - char buf[20]; - AsmScn_t *scn; - - /* Create a unique name. */ - snprintf (buf, sizeof (buf), ".data.%zu", cnt); - - /* Create the section. */ - scn = asm_newscn (ctx, buf, SHT_PROGBITS, SHF_ALLOC | SHF_WRITE); - if (scn == NULL) - { - printf ("cannot create section \"%s\" in output file: %s\n", - buf, asm_errmsg (-1)); - asm_abort (ctx); - return 1; - } - - /* Add a name. */ - snprintf (buf, sizeof (buf), "%zu", cnt); - if (asm_newsym (scn, buf, sizeof (uint32_t), STT_OBJECT, - STB_GLOBAL) == NULL) - { - printf ("cannot create symbol \"%s\": %s\n", buf, asm_errmsg (-1)); - asm_abort (ctx); - return 1; - } - - /* Add some content. */ - if (asm_adduint32 (scn, cnt) != 0) - { - printf ("cannot create content of section \"%s\": %s\n", - buf, asm_errmsg (-1)); - asm_abort (ctx); - return 1; - } - } - - /* Create the output file. */ - if (asm_end (ctx) != 0) - { - printf ("cannot create output file: %s\n", asm_errmsg (-1)); - asm_abort (ctx); - return 1; - } - - if (result == 0) - result = WEXITSTATUS (system ("../src/elflint -q asm-tst5-out.o")); - - /* We don't need the file anymore. */ - unlink (fname); - - ebl_closebackend (ebl); - - return result; -} diff --git a/tests/asm-tst6.c b/tests/asm-tst6.c index 829cd90..bd9b362 100644 --- a/tests/asm-tst6.c +++ b/tests/asm-tst6.c @@ -19,7 +19,6 @@ # include #endif -#include ELFUTILS_HEADER(ebl) #include ELFUTILS_HEADER(asm) #include #include diff --git a/tests/asm-tst6.c.libasm-ebl b/tests/asm-tst6.c.libasm-ebl deleted file mode 100644 index bd9b362..0000000 --- a/tests/asm-tst6.c.libasm-ebl +++ /dev/null @@ -1,150 +0,0 @@ -/* Copyright (C) 2002-2012 Red Hat, Inc. - This file is part of elfutils. - Written by Ulrich Drepper , 2002. - - This file is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - elfutils is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include ELFUTILS_HEADER(asm) -#include -#include -#include -#include -#include - -#include - - -static const char fname[] = "asm-tst6-out.o"; - - -int -main (void) -{ - AsmCtx_t *ctx; - int result = 0; - size_t cnt; - - elf_version (EV_CURRENT); - - Ebl *ebl = ebl_openbackend_machine (EM_386); - if (ebl == NULL) - { - puts ("cannot open backend library"); - return 1; - } - - ctx = asm_begin (fname, ebl, false); - if (ctx == NULL) - { - printf ("cannot create assembler context: %s\n", asm_errmsg (-1)); - return 1; - } - - for (cnt = 0; cnt < 22000; ++cnt) - { - char buf[512]; - AsmScnGrp_t *grp; - AsmScn_t *scn; - AsmSym_t *sym; - - snprintf (buf, sizeof (buf), ".grp%zu", cnt); - grp = asm_newscngrp (ctx, buf, NULL, 0); - if (grp == NULL) - { - printf ("cannot section group %zu: %s\n", cnt, asm_errmsg (-1)); - asm_abort (ctx); - return 1; - } - - scn = asm_newscn_ingrp (ctx, ".data", SHT_PROGBITS, - SHF_ALLOC | SHF_WRITE, grp); - if (scn == NULL) - { - printf ("cannot data section for group %zu: %s\n", - cnt, asm_errmsg (-1)); - asm_abort (ctx); - return 1; - } - - /* Add a name. */ - snprintf (buf, sizeof (buf), "%zu", cnt); - sym = asm_newsym (scn, buf, sizeof (uint32_t), STT_OBJECT, - STB_GLOBAL); - if (sym == NULL) - { - printf ("cannot create symbol \"%s\": %s\n", buf, asm_errmsg (-1)); - asm_abort (ctx); - return 1; - } - - /* Add some content. */ - if (asm_adduint32 (scn, cnt) != 0) - { - printf ("cannot create content of section \"%s\": %s\n", - buf, asm_errmsg (-1)); - asm_abort (ctx); - return 1; - } - - /* Now we have a symbol, use it as the signature. */ - if (asm_scngrp_newsignature (grp, sym) != 0) - { - printf ("cannot set signature for section group %zu: %s\n", - cnt, asm_errmsg (-1)); - asm_abort (ctx); - return 1; - } - - /* Create a phony debug info section. */ - scn = asm_newscn_ingrp (ctx, ".stab", SHT_PROGBITS, 0, grp); - if (scn == NULL) - { - printf ("cannot stab section for group %zu: %s\n", - cnt, asm_errmsg (-1)); - asm_abort (ctx); - return 1; - } - - /* Add some content. */ - if (asm_adduint32 (scn, cnt) != 0) - { - printf ("cannot create content of section \"%s\": %s\n", - buf, asm_errmsg (-1)); - asm_abort (ctx); - return 1; - } - } - - /* Create the output file. */ - if (asm_end (ctx) != 0) - { - printf ("cannot create output file: %s\n", asm_errmsg (-1)); - asm_abort (ctx); - return 1; - } - - if (result == 0) - result = WEXITSTATUS (system ("../src/elflint -q asm-tst6-out.o")); - - /* We don't need the file anymore. */ - unlink (fname); - - ebl_closebackend (ebl); - - return result; -} diff --git a/tests/asm-tst7.c b/tests/asm-tst7.c index 9017976..00cb2bf 100644 --- a/tests/asm-tst7.c +++ b/tests/asm-tst7.c @@ -21,7 +21,6 @@ #include #include -#include ELFUTILS_HEADER(ebl) #include ELFUTILS_HEADER(asm) #include #include diff --git a/tests/asm-tst7.c.libasm-ebl b/tests/asm-tst7.c.libasm-ebl deleted file mode 100644 index 00cb2bf..0000000 --- a/tests/asm-tst7.c.libasm-ebl +++ /dev/null @@ -1,181 +0,0 @@ -/* Copyright (C) 2002, 2005 Red Hat, Inc. - This file is part of elfutils. - Written by Ulrich Drepper , 2002. - - This file is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - elfutils is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include -#include -#include ELFUTILS_HEADER(asm) -#include -#include -#include - - -static const char fname[] = "asm-tst7-out.o"; - - -int -main (void) -{ - int result = 0; - size_t cnt; - AsmCtx_t *ctx; - Elf *elf; - int fd; - - elf_version (EV_CURRENT); - - Ebl *ebl = ebl_openbackend_machine (EM_386); - if (ebl == NULL) - { - puts ("cannot open backend library"); - return 1; - } - - ctx = asm_begin (fname, ebl, false); - if (ctx == NULL) - { - printf ("cannot create assembler context: %s\n", asm_errmsg (-1)); - return 1; - } - - if (asm_newcomsym (ctx, "commsym", 4, 16) == NULL) - { - printf ("cannot create common symbol: %s\n", asm_errmsg (-1)); - asm_abort (ctx); - return 1; - } - - /* Create the output file. */ - if (asm_end (ctx) != 0) - { - printf ("cannot create output file: %s\n", asm_errmsg (-1)); - asm_abort (ctx); - return 1; - } - - /* Check the file. */ - fd = open (fname, O_RDONLY); - if (fd == -1) - { - printf ("cannot open generated file: %m\n"); - result = 1; - goto out; - } - - elf = elf_begin (fd, ELF_C_READ, NULL); - if (elf == NULL) - { - printf ("cannot create ELF descriptor: %s\n", elf_errmsg (-1)); - result = 1; - goto out_close; - } - if (elf_kind (elf) != ELF_K_ELF) - { - puts ("not a valid ELF file"); - result = 1; - goto out_close2; - } - - for (cnt = 1; 1; ++cnt) - { - Elf_Scn *scn; - GElf_Shdr shdr_mem; - GElf_Shdr *shdr; - - scn = elf_getscn (elf, cnt); - if (scn == NULL) - { - printf ("cannot get section %zd: %s\n", cnt, elf_errmsg (-1)); - result = 1; - continue; - } - - shdr = gelf_getshdr (scn, &shdr_mem); - if (shdr == NULL) - { - printf ("cannot get section header for section %zd: %s\n", - cnt, elf_errmsg (-1)); - result = 1; - continue; - } - /* We are looking for the symbol table. */ - if (shdr->sh_type != SHT_SYMTAB) - continue; - - for (cnt = 1; cnt< (shdr->sh_size - / gelf_fsize (elf, ELF_T_SYM, 1, EV_CURRENT)); - ++cnt) - { - GElf_Sym sym_mem; - GElf_Sym *sym; - - if (cnt > 1) - { - puts ("too many symbol"); - result = 1; - break; - } - - sym = gelf_getsym (elf_getdata (scn, NULL), cnt, &sym_mem); - if (sym == NULL) - { - printf ("cannot get symbol %zu: %s\n", cnt, elf_errmsg (-1)); - result = 1; - } - else - { - if (sym->st_shndx != SHN_COMMON) - { - printf ("expected common symbol, got section %u\n", - (unsigned int) sym->st_shndx); - result = 1; - } - - if (sym->st_value != 16) - { - printf ("requested alignment 16, is %" PRIuMAX "\n", - (uintmax_t) sym->st_value); - result = 1; - } - - if (sym->st_size != 4) - { - printf ("requested size 4, is %" PRIuMAX "\n", - (uintmax_t) sym->st_value); - result = 1; - } - } - } - - break; - } - - out_close2: - elf_end (elf); - out_close: - close (fd); - out: - /* We don't need the file anymore. */ - unlink (fname); - - ebl_closebackend (ebl); - - return result; -} diff --git a/tests/asm-tst8.c b/tests/asm-tst8.c index a65509f..4fb0d99 100644 --- a/tests/asm-tst8.c +++ b/tests/asm-tst8.c @@ -21,7 +21,6 @@ #include #include -#include ELFUTILS_HEADER(ebl) #include ELFUTILS_HEADER(asm) #include #include diff --git a/tests/asm-tst8.c.libasm-ebl b/tests/asm-tst8.c.libasm-ebl deleted file mode 100644 index 4fb0d99..0000000 --- a/tests/asm-tst8.c.libasm-ebl +++ /dev/null @@ -1,189 +0,0 @@ -/* Copyright (C) 2002, 2005 Red Hat, Inc. - This file is part of elfutils. - Written by Ulrich Drepper , 2002. - - This file is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - elfutils is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include -#include -#include ELFUTILS_HEADER(asm) -#include -#include -#include - - -static const char fname[] = "asm-tst8-out.o"; - - -int -main (void) -{ - int result = 0; - size_t cnt; - AsmCtx_t *ctx; - Elf *elf; - int fd; - - elf_version (EV_CURRENT); - - Ebl *ebl = ebl_openbackend_machine (EM_386); - if (ebl == NULL) - { - puts ("cannot open backend library"); - return 1; - } - - ctx = asm_begin (fname, ebl, false); - if (ctx == NULL) - { - printf ("cannot create assembler context: %s\n", asm_errmsg (-1)); - return 1; - } - - if (asm_newabssym (ctx, "tst8-out.s", 4, 0xfeedbeef, STT_FILE, STB_LOCAL) - == NULL) - { - printf ("cannot create absolute symbol: %s\n", asm_errmsg (-1)); - asm_abort (ctx); - return 1; - } - - /* Create the output file. */ - if (asm_end (ctx) != 0) - { - printf ("cannot create output file: %s\n", asm_errmsg (-1)); - asm_abort (ctx); - return 1; - } - - /* Check the file. */ - fd = open (fname, O_RDONLY); - if (fd == -1) - { - printf ("cannot open generated file: %m\n"); - result = 1; - goto out; - } - - elf = elf_begin (fd, ELF_C_READ, NULL); - if (elf == NULL) - { - printf ("cannot create ELF descriptor: %s\n", elf_errmsg (-1)); - result = 1; - goto out_close; - } - if (elf_kind (elf) != ELF_K_ELF) - { - puts ("not a valid ELF file"); - result = 1; - goto out_close2; - } - - for (cnt = 1; 1; ++cnt) - { - Elf_Scn *scn; - GElf_Shdr shdr_mem; - GElf_Shdr *shdr; - - scn = elf_getscn (elf, cnt); - if (scn == NULL) - { - printf ("cannot get section %zd: %s\n", cnt, elf_errmsg (-1)); - result = 1; - continue; - } - - shdr = gelf_getshdr (scn, &shdr_mem); - if (shdr == NULL) - { - printf ("cannot get section header for section %zd: %s\n", - cnt, elf_errmsg (-1)); - result = 1; - continue; - } - /* We are looking for the symbol table. */ - if (shdr->sh_type != SHT_SYMTAB) - continue; - - for (cnt = 1; cnt< (shdr->sh_size - / gelf_fsize (elf, ELF_T_SYM, 1, EV_CURRENT)); - ++cnt) - { - GElf_Sym sym_mem; - GElf_Sym *sym; - - if (cnt > 1) - { - puts ("too many symbol"); - result = 1; - break; - } - - sym = gelf_getsym (elf_getdata (scn, NULL), cnt, &sym_mem); - if (sym == NULL) - { - printf ("cannot get symbol %zu: %s\n", cnt, elf_errmsg (-1)); - result = 1; - } - else - { - if (sym->st_shndx != SHN_ABS) - { - printf ("expected common symbol, got section %u\n", - (unsigned int) sym->st_shndx); - result = 1; - } - - if (sym->st_value != 0xfeedbeef) - { - printf ("requested value 0xfeedbeef, is %#" PRIxMAX "\n", - (uintmax_t) sym->st_value); - result = 1; - } - - if (sym->st_size != 4) - { - printf ("requested size 4, is %" PRIuMAX "\n", - (uintmax_t) sym->st_value); - result = 1; - } - - if (GELF_ST_TYPE (sym->st_info) != STT_FILE) - { - printf ("requested type FILE, is %u\n", - (unsigned int) GELF_ST_TYPE (sym->st_info)); - result = 1; - } - } - } - - break; - } - - out_close2: - elf_end (elf); - out_close: - close (fd); - out: - /* We don't need the file anymore. */ - unlink (fname); - - ebl_closebackend (ebl); - - return result; -} diff --git a/tests/asm-tst9.c b/tests/asm-tst9.c index 681e872..b6d0e43 100644 --- a/tests/asm-tst9.c +++ b/tests/asm-tst9.c @@ -21,7 +21,6 @@ #include #include -#include ELFUTILS_HEADER(ebl) #include ELFUTILS_HEADER(asm) #include #include diff --git a/tests/asm-tst9.c.libasm-ebl b/tests/asm-tst9.c.libasm-ebl deleted file mode 100644 index b6d0e43..0000000 --- a/tests/asm-tst9.c.libasm-ebl +++ /dev/null @@ -1,335 +0,0 @@ -/* Copyright (C) 2002-2010 Red Hat, Inc. - This file is part of elfutils. - Written by Ulrich Drepper , 2002. - - This file is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - elfutils is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include -#include -#include ELFUTILS_HEADER(asm) -#include -#include -#include -#include - - -static const char fname[] = "asm-tst9-out.o"; - - -static int32_t input[] = - { - 0, 1, 129, 510, 2000, 33000, 0x7ffffff, 0x7fffffff - }; -#define ninput (sizeof (input) / sizeof (input[0])) - - -static const GElf_Ehdr expected_ehdr = - { - .e_ident = { [EI_MAG0] = ELFMAG0, - [EI_MAG1] = ELFMAG1, - [EI_MAG2] = ELFMAG2, - [EI_MAG3] = ELFMAG3, - [EI_CLASS] = ELFCLASS32, - [EI_DATA] = ELFDATA2LSB, - [EI_VERSION] = EV_CURRENT }, - .e_type = ET_REL, - .e_machine = EM_386, - .e_version = EV_CURRENT, - .e_shoff = 180, - .e_ehsize = sizeof (Elf32_Ehdr), - .e_shentsize = sizeof (Elf32_Shdr), - .e_shnum = 3, - .e_shstrndx = 2 - }; - - -static const char *scnnames[3] = - { - [0] = "", - [1] = ".data", - [2] = ".shstrtab" - }; - - -static const char expecteddata[] = - { - 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x7f, - 0x81, 0x01, 0x81, 0x01, 0xff, 0xfe, 0xff, 0xff, 0x0f, 0xff, 0x7e, 0xfe, - 0x03, 0xfe, 0x03, 0x82, 0xfc, 0xff, 0xff, 0x0f, 0x82, 0x7c, 0xd0, 0x0f, - 0xd0, 0x0f, 0xb0, 0xf0, 0xff, 0xff, 0x0f, 0xb0, 0x70, 0xe8, 0x81, 0x02, - 0xe8, 0x81, 0x02, 0x98, 0xfe, 0xfd, 0xff, 0x0f, 0x98, 0xfe, 0x7d, 0xff, - 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0x3f, 0x81, 0x80, 0x80, 0xc0, 0x0f, - 0x81, 0x80, 0x80, 0x40, 0xff, 0xff, 0xff, 0xff, 0x07, 0xff, 0xff, 0xff, - 0xff, 0x07, 0x81, 0x80, 0x80, 0x80, 0x08, 0x81, 0x80, 0x80, 0x80, 0x78 - }; - - -int -main (void) -{ - AsmCtx_t *ctx; - AsmScn_t *scn; - int result = 0; - int fd; - Elf *elf; - GElf_Ehdr ehdr_mem; - GElf_Ehdr *ehdr; - size_t cnt; - - elf_version (EV_CURRENT); - - Ebl *ebl = ebl_openbackend_machine (EM_386); - if (ebl == NULL) - { - puts ("cannot open backend library"); - return 1; - } - - ctx = asm_begin (fname, ebl, false); - if (ctx == NULL) - { - printf ("cannot create assembler context: %s\n", asm_errmsg (-1)); - return 1; - } - - /* Create two sections. */ - scn = asm_newscn (ctx, ".data", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE); - if (scn == NULL) - { - printf ("cannot create section in output file: %s\n", asm_errmsg (-1)); - asm_abort (ctx); - return 1; - } - - /* Special alignment for the .text section. */ - if (asm_align (scn, 16) != 0) - { - printf ("cannot align .text section: %s\n", asm_errmsg (-1)); - result = 1; - } - - /* Add a few ULEB128 and SLEB128 numbers. */ - for (cnt = 0; cnt < ninput; ++cnt) - { - if (asm_adduleb128 (scn, input[cnt]) != 0) - { - printf ("cannot insert uleb %" PRIu32 ": %s\n", - (uint32_t) input[cnt], asm_errmsg (-1)); - result = 1; - } - - if (asm_addsleb128 (scn, input[cnt]) != 0) - { - printf ("cannot insert sleb %" PRId32 ": %s\n", - input[cnt], asm_errmsg (-1)); - result = 1; - } - - if (asm_adduleb128 (scn, -input[cnt]) != 0) - { - printf ("cannot insert uleb %" PRIu32 ": %s\n", - (uint32_t) -input[cnt], asm_errmsg (-1)); - result = 1; - } - - if (asm_addsleb128 (scn, -input[cnt]) != 0) - { - printf ("cannot insert sleb %" PRId32 ": %s\n", - -input[cnt], asm_errmsg (-1)); - result = 1; - } - } - - /* Create the output file. */ - if (asm_end (ctx) != 0) - { - printf ("cannot create output file: %s\n", asm_errmsg (-1)); - asm_abort (ctx); - return 1; - } - - /* Check the file. */ - fd = open (fname, O_RDONLY); - if (fd == -1) - { - printf ("cannot open generated file: %m\n"); - result = 1; - goto out; - } - - elf = elf_begin (fd, ELF_C_READ, NULL); - if (elf == NULL) - { - printf ("cannot create ELF descriptor: %s\n", elf_errmsg (-1)); - result = 1; - goto out_close; - } - if (elf_kind (elf) != ELF_K_ELF) - { - puts ("not a valid ELF file"); - result = 1; - goto out_close2; - } - - ehdr = gelf_getehdr (elf, &ehdr_mem); - if (ehdr == NULL) - { - printf ("cannot get ELF header: %s\n", elf_errmsg (-1)); - result = 1; - goto out_close2; - } - - if (memcmp (ehdr, &expected_ehdr, sizeof (GElf_Ehdr)) != 0) - { - puts ("ELF header does not match"); - result = 1; - goto out_close2; - } - - for (cnt = 1; cnt < 3; ++cnt) - { - Elf_Scn *escn; - GElf_Shdr shdr_mem; - GElf_Shdr *shdr; - - escn = elf_getscn (elf, cnt); - if (escn == NULL) - { - printf ("cannot get section %zd: %s\n", cnt, elf_errmsg (-1)); - result = 1; - continue; - } - - shdr = gelf_getshdr (escn, &shdr_mem); - if (shdr == NULL) - { - printf ("cannot get section header for section %zd: %s\n", - cnt, elf_errmsg (-1)); - result = 1; - continue; - } - - if (strcmp (elf_strptr (elf, ehdr->e_shstrndx, shdr->sh_name), - scnnames[cnt]) != 0) - { - printf ("section %zd's name differs: %s vs %s\n", cnt, - elf_strptr (elf, ehdr->e_shstrndx, shdr->sh_name), - scnnames[cnt]); - result = 1; - } - - if (shdr->sh_type != (cnt == 2 ? SHT_STRTAB : SHT_PROGBITS)) - { - printf ("section %zd's type differs\n", cnt); - result = 1; - } - - if ((cnt == 1 && shdr->sh_flags != (SHF_ALLOC | SHF_WRITE)) - || (cnt == 2 && shdr->sh_flags != 0)) - { - printf ("section %zd's flags differs\n", cnt); - result = 1; - } - - if (shdr->sh_addr != 0) - { - printf ("section %zd's address differs\n", cnt); - result = 1; - } - - if ((cnt == 1 && shdr->sh_offset != ((sizeof (Elf32_Ehdr) + 15) & ~15)) - || (cnt == 2 - && shdr->sh_offset != (((sizeof (Elf32_Ehdr) + 15) & ~15) - + sizeof (expecteddata)))) - { - printf ("section %zd's offset differs\n", cnt); - result = 1; - } - - if ((cnt == 1 && shdr->sh_size != sizeof (expecteddata)) - || (cnt == 2 && shdr->sh_size != 17)) - { - printf ("section %zd's size differs\n", cnt); - result = 1; - } - - if (shdr->sh_link != 0) - { - printf ("section %zd's link differs\n", cnt); - result = 1; - } - - if (shdr->sh_info != 0) - { - printf ("section %zd's info differs\n", cnt); - result = 1; - } - - if ((cnt == 1 && shdr->sh_addralign != 16) - || (cnt != 1 && shdr->sh_addralign != 1)) - { - printf ("section %zd's addralign differs\n", cnt); - result = 1; - } - - if (shdr->sh_entsize != 0) - { - printf ("section %zd's entsize differs\n", cnt); - result = 1; - } - - if (cnt == 1) - { - Elf_Data *data = elf_getdata (escn, NULL); - - if (data == NULL) - { - printf ("cannot get data of section %zd\n", cnt); - result = 1; - } - else - { - if (data->d_size != sizeof (expecteddata)) - { - printf ("data block size of section %zd wrong: got %zd, " - "expected 96\n", cnt, data->d_size); - result = 1; - } - - if (memcmp (data->d_buf, expecteddata, sizeof (expecteddata)) - != 0) - { - printf ("data block content of section %zd wrong\n", cnt); - result = 1; - } - } - } - } - - out_close2: - elf_end (elf); - out_close: - close (fd); - out: - /* We don't need the file anymore. */ - unlink (fname); - - ebl_closebackend (ebl); - - return result; -} diff --git a/tests/run-debuginfod-find.sh b/tests/run-debuginfod-find.sh index c8979e9..0ade03b 100755 --- a/tests/run-debuginfod-find.sh +++ b/tests/run-debuginfod-find.sh @@ -153,11 +153,8 @@ cmp $filename F/prog2 cat vlog grep -q Progress vlog tempfiles vlog -filename=`testrun env DEBUGINFOD_PROGRESS=1 ${abs_top_builddir}/debuginfod/debuginfod-find executable $BUILDID2 2>vlog2` +filename=`testrun ${abs_top_builddir}/debuginfod/debuginfod-find executable $BUILDID2` cmp $filename F/prog2 -cat vlog2 -grep -q Downloading vlog2 -tempfiles vlog2 filename=`testrun ${abs_top_builddir}/debuginfod/debuginfod-find source $BUILDID2 ${PWD}/prog2.c` cmp $filename ${PWD}/prog2.c diff --git a/tests/run-debuginfod-find.sh.debuginfod-timeout-progress b/tests/run-debuginfod-find.sh.debuginfod-timeout-progress deleted file mode 100755 index 0ade03b..0000000 --- a/tests/run-debuginfod-find.sh.debuginfod-timeout-progress +++ /dev/null @@ -1,324 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2019 Red Hat, Inc. -# This file is part of elfutils. -# -# This file is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# elfutils is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -. $srcdir/test-subr.sh # includes set -e - -DB=${PWD}/.debuginfod_tmp.sqlite -tempfiles $DB -export DEBUGINFOD_CACHE_PATH=${PWD}/.client_cache - -PID1=0 -PID2=0 - -cleanup() -{ - if [ $PID1 -ne 0 ]; then kill $PID1; wait $PID1; fi - if [ $PID2 -ne 0 ]; then kill $PID2; wait $PID2; fi - - rm -rf F R L ${PWD}/.client_cache* - exit_cleanup -} - -# clean up trash if we were aborted early -trap cleanup 0 1 2 3 5 9 15 - -# find an unused port number -while true; do - PORT1=`expr '(' $RANDOM % 1000 ')' + 9000` - ss -atn | fgrep ":$PORT1" || break -done - -# We want to run debuginfod in the background. We also want to start -# it with the same check/installcheck-sensitive LD_LIBRARY_PATH stuff -# that the testrun alias sets. But: we if we just use -# testrun .../debuginfod -# it runs in a subshell, with different pid, so not helpful. -# -# So we gather the LD_LIBRARY_PATH with this cunning trick: -ldpath=`testrun sh -c 'echo $LD_LIBRARY_PATH'` - -mkdir F R L -# not tempfiles F R L - they are directories which we clean up manually -ln -s ${abs_builddir}/dwfllines L/foo # any program not used elsewhere in this test - -wait_ready() -{ - port=$1; - what=$2; - value=$3; - timeout=20; - - echo "Wait $timeout seconds on $port for metric $what to change to $value" - while [ $timeout -gt 0 ]; do - mvalue="$(curl -s http://127.0.0.1:$port/metrics \ - | grep "$what" | awk '{print $NF}')" - if [ -z "$mvalue" ]; then mvalue=0; fi - echo "metric $what: $mvalue" - if [ "$mvalue" -eq "$value" ]; then - break; - fi - sleep 0.5; - ((timeout--)); - done; - - if [ $timeout -eq 0 ]; then - echo "metric $what never changed to $value on port $port" - exit 1; - fi -} - -env LD_LIBRARY_PATH=$ldpath DEBUGINFOD_URLS= ${abs_builddir}/../debuginfod/debuginfod -F -R -d $DB -p $PORT1 -t0 -g0 R F L & -PID1=$! -# Server must become ready -wait_ready $PORT1 'ready' 1 -export DEBUGINFOD_URLS=http://127.0.0.1:$PORT1/ # or without trailing / - -# Be patient when run on a busy machine things might take a bit. -export DEBUGINFOD_TIMEOUT=10 - -# We use -t0 and -g0 here to turn off time-based scanning & grooming. -# For testing purposes, we just sic SIGUSR1 / SIGUSR2 at the process. - -######################################################################## - -# Compile a simple program, strip its debuginfo and save the build-id. -# Also move the debuginfo into another directory so that elfutils -# cannot find it without debuginfod. -echo "int main() { return 0; }" > ${PWD}/prog.c -tempfiles prog.c -gcc -g -o prog ${PWD}/prog.c - ${abs_top_builddir}/src/strip -g -f prog.debug ${PWD}/prog -BUILDID=`env LD_LIBRARY_PATH=$ldpath ${abs_builddir}/../src/readelf \ - -a prog | grep 'Build ID' | cut -d ' ' -f 7` - -mv prog F -mv prog.debug F -kill -USR1 $PID1 -# Wait till both files are in the index. -wait_ready $PORT1 'thread_work_total{file="F"}' 2 - -######################################################################## - -# Test whether elfutils, via the debuginfod client library dlopen hooks, -# is able to fetch debuginfo from the local debuginfod. -testrun ${abs_builddir}/debuginfod_build_id_find -e F/prog 1 - -######################################################################## - -# Test whether debuginfod-find is able to fetch those files. -rm -rf $DEBUGINFOD_CACHE_PATH # clean it from previous tests -filename=`testrun ${abs_top_builddir}/debuginfod/debuginfod-find debuginfo $BUILDID` -cmp $filename F/prog.debug - -filename=`testrun ${abs_top_builddir}/debuginfod/debuginfod-find executable $BUILDID` -cmp $filename F/prog - -filename=`testrun ${abs_top_builddir}/debuginfod/debuginfod-find source $BUILDID ${PWD}/prog.c` -cmp $filename ${PWD}/prog.c - -######################################################################## - -# Add artifacts to the search paths and test whether debuginfod finds them while already running. - -# Build another, non-stripped binary -echo "int main() { return 0; }" > ${PWD}/prog2.c -tempfiles prog2.c -gcc -g -o prog2 ${PWD}/prog2.c -BUILDID2=`env LD_LIBRARY_PATH=$ldpath ${abs_builddir}/../src/readelf \ - -a prog2 | grep 'Build ID' | cut -d ' ' -f 7` - -mv prog2 F -kill -USR1 $PID1 -# Now there should be 3 files in the index -wait_ready $PORT1 'thread_work_total{file="F"}' 3 - -# Rerun same tests for the prog2 binary -filename=`testrun ${abs_top_builddir}/debuginfod/debuginfod-find -v debuginfo $BUILDID2 2>vlog` -cmp $filename F/prog2 -cat vlog -grep -q Progress vlog -tempfiles vlog -filename=`testrun ${abs_top_builddir}/debuginfod/debuginfod-find executable $BUILDID2` -cmp $filename F/prog2 -filename=`testrun ${abs_top_builddir}/debuginfod/debuginfod-find source $BUILDID2 ${PWD}/prog2.c` -cmp $filename ${PWD}/prog2.c - -cp -rp ${abs_srcdir}/debuginfod-rpms R -kill -USR1 $PID1 -# All rpms need to be in the index -rpms=$(find R -name \*rpm | wc -l) -wait_ready $PORT1 'scanned_total{source="rpm"}' $rpms - -kill -USR1 $PID1 # two hits of SIGUSR1 may be needed to resolve .debug->dwz->srefs -# Expect all source files found in the rpms (they are all called hello.c :) -# We will need to extract all rpms (in their own directory) and could all -# sources referenced in the .debug files. -mkdir extracted -cd extracted -subdir=0; -newrpms=$(find ../R -name \*\.rpm) -for i in $newrpms; do - subdir=$[$subdir+1]; - mkdir $subdir; - cd $subdir; - ls -lah ../$i - rpm2cpio ../$i | cpio -id; - cd ..; -done -sourcefiles=$(find -name \*\\.debug \ - | env LD_LIBRARY_PATH=$ldpath xargs \ - ${abs_top_builddir}/src/readelf --debug-dump=decodedline \ - | grep mtime: | wc --lines) -cd .. -rm -rf extracted - -wait_ready $PORT1 'found_sourcerefs_total{source="rpm"}' $sourcefiles - -# Run a bank of queries against the debuginfod-rpms test cases - -rpm_test() { - __BUILDID=$1 - __SOURCEPATH=$2 - __SOURCESHA1=$3 - - filename=`testrun ${abs_top_builddir}/debuginfod/debuginfod-find executable $__BUILDID` - buildid=`env LD_LIBRARY_PATH=$ldpath ${abs_builddir}/../src/readelf \ - -a $filename | grep 'Build ID' | cut -d ' ' -f 7` - test $__BUILDID = $buildid - - filename=`testrun ${abs_top_builddir}/debuginfod/debuginfod-find debuginfo $__BUILDID` - buildid=`env LD_LIBRARY_PATH=$ldpath ${abs_builddir}/../src/readelf \ - -a $filename | grep 'Build ID' | cut -d ' ' -f 7` - test $__BUILDID = $buildid - - filename=`testrun ${abs_top_builddir}/debuginfod/debuginfod-find source $__BUILDID $__SOURCEPATH` - hash=`cat $filename | sha1sum | awk '{print $1}'` - test $__SOURCESHA1 = $hash -} - - -# common source file sha1 -SHA=f4a1a8062be998ae93b8f1cd744a398c6de6dbb1 -# fedora30 -rpm_test c36708a78618d597dee15d0dc989f093ca5f9120 /usr/src/debug/hello2-1.0-2.x86_64/hello.c $SHA -rpm_test 41a236eb667c362a1c4196018cc4581e09722b1b /usr/src/debug/hello2-1.0-2.x86_64/hello.c $SHA -# rhel7 -rpm_test bc1febfd03ca05e030f0d205f7659db29f8a4b30 /usr/src/debug/hello-1.0/hello.c $SHA -rpm_test f0aa15b8aba4f3c28cac3c2a73801fefa644a9f2 /usr/src/debug/hello-1.0/hello.c $SHA -# rhel6 -rpm_test bbbf92ebee5228310e398609c23c2d7d53f6e2f9 /usr/src/debug/hello-1.0/hello.c $SHA -rpm_test d44d42cbd7d915bc938c81333a21e355a6022fb7 /usr/src/debug/hello-1.0/hello.c $SHA - -RPM_BUILDID=d44d42cbd7d915bc938c81333a21e355a6022fb7 # in rhel6/ subdir, for a later test - - -######################################################################## - -# Drop some of the artifacts, run a groom cycle; confirm that -# debuginfod has forgotten them, but remembers others - -rm -r R/debuginfod-rpms/rhel6/* -kill -USR2 $PID1 # groom cycle -# Expect 3 rpms to be deleted by the groom -wait_ready $PORT1 'groom{statistic="file d/e"}' 3 - -rm -rf $DEBUGINFOD_CACHE_PATH # clean it from previous tests - -testrun ${abs_top_builddir}/debuginfod/debuginfod-find executable $RPM_BUILDID && false || true - -testrun ${abs_top_builddir}/debuginfod/debuginfod-find executable $BUILDID2 - -######################################################################## - -# Federation mode - -# find another unused port -while true; do - PORT2=`expr '(' $RANDOM % 1000 ')' + 9000` - ss -atn | fgrep ":$PORT2" || break -done - -export DEBUGINFOD_CACHE_PATH=${PWD}/.client_cache2 -mkdir -p $DEBUGINFOD_CACHE_PATH -# NB: inherits the DEBUGINFOD_URLS to the first server -# NB: run in -L symlink-following mode for the L subdir -env LD_LIBRARY_PATH=$ldpath ${abs_builddir}/../debuginfod/debuginfod -F -d ${DB}_2 -p $PORT2 -L L & -PID2=$! -tempfiles ${DB}_2 -wait_ready $PORT2 'ready' 1 - -# have clients contact the new server -export DEBUGINFOD_URLS=http://127.0.0.1:$PORT2 -rm -rf $DEBUGINFOD_CACHE_PATH -testrun ${abs_top_builddir}/debuginfod/debuginfod-find debuginfo $BUILDID - -# confirm that first server can't resolve symlinked info in L/ but second can -BUILDID=`env LD_LIBRARY_PATH=$ldpath ${abs_builddir}/../src/readelf \ - -a L/foo | grep 'Build ID' | cut -d ' ' -f 7` -file L/foo -file -L L/foo -export DEBUGINFOD_URLS=http://127.0.0.1:$PORT1 -rm -rf $DEBUGINFOD_CACHE_PATH -testrun ${abs_top_builddir}/debuginfod/debuginfod-find debuginfo $BUILDID && false || true -export DEBUGINFOD_URLS=http://127.0.0.1:$PORT2 -testrun ${abs_top_builddir}/debuginfod/debuginfod-find debuginfo $BUILDID - - -# test parallel queries in client -export DEBUGINFOD_CACHE_PATH=${PWD}/.client_cache3 -mkdir -p $DEBUGINFOD_CACHE_PATH -export DEBUGINFOD_URLS="BAD http://127.0.0.1:$PORT1 127.0.0.1:$PORT1 http://127.0.0.1:$PORT2 DNE" - -testrun ${abs_builddir}/debuginfod_build_id_find -e F/prog2 1 - -######################################################################## - -# Fetch some metrics, if curl program is installed -if type curl 2>/dev/null; then - curl http://127.0.0.1:$PORT1/badapi - curl http://127.0.0.1:$PORT1/metrics - curl http://127.0.0.1:$PORT2/metrics - curl http://127.0.0.1:$PORT1/metrics | grep -q 'http_responses_total.*result.*error' - curl http://127.0.0.1:$PORT2/metrics | grep -q 'http_responses_total.*result.*upstream' -fi - -######################################################################## - -# Run the tests again without the servers running. The target file should -# be found in the cache. - -kill -INT $PID1 $PID2 -wait $PID1 $PID2 -PID1=0 -PID2=0 -tempfiles .debuginfod_* - -testrun ${abs_builddir}/debuginfod_build_id_find -e F/prog2 1 - -######################################################################## - -# Trigger a cache clean and run the tests again. The clients should be unable to -# find the target. -echo 0 > $DEBUGINFOD_CACHE_PATH/cache_clean_interval_s -echo 0 > $DEBUGINFOD_CACHE_PATH/max_unused_age_s - -testrun ${abs_builddir}/debuginfod_build_id_find -e F/prog 1 - -testrun ${abs_top_builddir}/debuginfod/debuginfod-find debuginfo $BUILDID2 && false || true - -exit 0