diff options
Diffstat (limited to 'binreloc.c')
-rw-r--r-- | binreloc.c | 800 |
1 files changed, 392 insertions, 408 deletions
@@ -23,247 +23,245 @@ #include "binreloc.h" G_BEGIN_DECLS - - /** @internal * Find the canonical filename of the executable. Returns the filename * (which must be freed) or NULL on error. If the parameter 'error' is * not NULL, the error code will be stored there, if an error occured. */ -static char * -_br_find_exe (GbrInitError *error) +static char *_br_find_exe(GbrInitError * error) { - char *path, *path2, *line, *result; - size_t buf_size; - ssize_t size; - struct stat stat_buf; - FILE *f; - - /* Read from /proc/self/exe (symlink) */ - if (sizeof (path) > SSIZE_MAX) - buf_size = SSIZE_MAX - 1; - else - buf_size = PATH_MAX - 1; - path = (char *) g_try_malloc (buf_size); - if (path == NULL) { - /* Cannot allocate memory. */ - if (error) - *error = GBR_INIT_ERROR_NOMEM; - return NULL; + char *path, *path2, *line, *result; + size_t buf_size; + ssize_t size; + struct stat stat_buf; + FILE *f; + + /* Read from /proc/self/exe (symlink) */ + if (sizeof(path) > SSIZE_MAX) + buf_size = SSIZE_MAX - 1; + else + buf_size = PATH_MAX - 1; + path = (char *) g_try_malloc(buf_size); + if (path == NULL) { + /* Cannot allocate memory. */ + if (error) + *error = GBR_INIT_ERROR_NOMEM; + return NULL; + } + path2 = (char *) g_try_malloc(buf_size); + if (path2 == NULL) { + /* Cannot allocate memory. */ + if (error) + *error = GBR_INIT_ERROR_NOMEM; + g_free(path); + return NULL; + } + + strncpy(path2, "/proc/self/exe", buf_size - 1); + + while (1) { + int i; + + size = readlink(path2, path, buf_size - 1); + if (size == -1) { + /* Error. */ + g_free(path2); + break; } - path2 = (char *) g_try_malloc (buf_size); - if (path2 == NULL) { - /* Cannot allocate memory. */ - if (error) - *error = GBR_INIT_ERROR_NOMEM; - g_free (path); - return NULL; + + /* readlink() success. */ + path[size] = '\0'; + + /* Check whether the symlink's target is also a symlink. + * We want to get the final target. */ + i = stat(path, &stat_buf); + if (i == -1) { + /* Error. */ + g_free(path2); + break; } - strncpy (path2, "/proc/self/exe", buf_size - 1); - - while (1) { - int i; - - size = readlink (path2, path, buf_size - 1); - if (size == -1) { - /* Error. */ - g_free (path2); - break; - } - - /* readlink() success. */ - path[size] = '\0'; - - /* Check whether the symlink's target is also a symlink. - * We want to get the final target. */ - i = stat (path, &stat_buf); - if (i == -1) { - /* Error. */ - g_free (path2); - break; - } - - /* stat() success. */ - if (!S_ISLNK (stat_buf.st_mode)) { - /* path is not a symlink. Done. */ - g_free (path2); - return path; - } - - /* path is a symlink. Continue loop and resolve this. */ - strncpy (path, path2, buf_size - 1); + /* stat() success. */ + if (!S_ISLNK(stat_buf.st_mode)) { + /* path is not a symlink. Done. */ + g_free(path2); + return path; } + /* path is a symlink. Continue loop and resolve this. */ + strncpy(path, path2, buf_size - 1); + } + + + /* readlink() or stat() failed; this can happen when the program is + * running in Valgrind 2.2. Read from /proc/self/maps as fallback. */ + + buf_size = PATH_MAX + 128; + line = (char *) g_try_realloc(path, buf_size); + if (line == NULL) { + /* Cannot allocate memory. */ + g_free(path); + if (error) + *error = GBR_INIT_ERROR_NOMEM; + return NULL; + } + + f = fopen("/proc/self/maps", "r"); + if (f == NULL) { + g_free(line); + if (error) + *error = GBR_INIT_ERROR_OPEN_MAPS; + return NULL; + } + + /* The first entry should be the executable name. */ + result = fgets(line, (int) buf_size, f); + if (result == NULL) { + fclose(f); + g_free(line); + if (error) + *error = GBR_INIT_ERROR_READ_MAPS; + return NULL; + } + + /* Get rid of newline character. */ + buf_size = strlen(line); + if (buf_size <= 0) { + /* Huh? An empty string? */ + fclose(f); + g_free(line); + if (error) + *error = GBR_INIT_ERROR_INVALID_MAPS; + return NULL; + } + if (line[buf_size - 1] == 10) + line[buf_size - 1] = 0; + + /* Extract the filename; it is always an absolute path. */ + path = strchr(line, '/'); + + /* Sanity check. */ + if (strstr(line, " r-xp ") == NULL || path == NULL) { + fclose(f); + g_free(line); + if (error) + *error = GBR_INIT_ERROR_INVALID_MAPS; + return NULL; + } + + path = g_strdup(path); + g_free(line); + fclose(f); + return path; +} - /* readlink() or stat() failed; this can happen when the program is - * running in Valgrind 2.2. Read from /proc/self/maps as fallback. */ - buf_size = PATH_MAX + 128; - line = (char *) g_try_realloc (path, buf_size); - if (line == NULL) { - /* Cannot allocate memory. */ - g_free (path); - if (error) - *error = GBR_INIT_ERROR_NOMEM; - return NULL; - } +/** @internal + * Find the canonical filename of the executable which owns symbol. + * Returns a filename which must be freed, or NULL on error. + */ +static char *_br_find_exe_for_symbol(const void *symbol, + GbrInitError * error) +{ +#define SIZE PATH_MAX + 100 + FILE *f; + size_t address_string_len; + char *address_string, line[SIZE], *found; - f = fopen ("/proc/self/maps", "r"); - if (f == NULL) { - g_free (line); - if (error) - *error = GBR_INIT_ERROR_OPEN_MAPS; - return NULL; - } + if (symbol == NULL) + return (char *) NULL; - /* The first entry should be the executable name. */ - result = fgets (line, (int) buf_size, f); - if (result == NULL) { - fclose (f); - g_free (line); - if (error) - *error = GBR_INIT_ERROR_READ_MAPS; - return NULL; - } + f = fopen("/proc/self/maps", "r"); + if (f == NULL) + return (char *) NULL; - /* Get rid of newline character. */ - buf_size = strlen (line); - if (buf_size <= 0) { - /* Huh? An empty string? */ - fclose (f); - g_free (line); - if (error) - *error = GBR_INIT_ERROR_INVALID_MAPS; - return NULL; - } - if (line[buf_size - 1] == 10) - line[buf_size - 1] = 0; + address_string_len = 4; + address_string = (char *) g_try_malloc(address_string_len); + found = (char *) NULL; + + while (!feof(f)) { + char *start_addr, *end_addr, *end_addr_end, *file; + void *start_addr_p, *end_addr_p; + size_t len; - /* Extract the filename; it is always an absolute path. */ - path = strchr (line, '/'); + if (fgets(line, SIZE, f) == NULL) + break; /* Sanity check. */ - if (strstr (line, " r-xp ") == NULL || path == NULL) { - fclose (f); - g_free (line); - if (error) - *error = GBR_INIT_ERROR_INVALID_MAPS; - return NULL; + if (strstr(line, " r-xp ") == NULL || strchr(line, '/') == NULL) + continue; + + /* Parse line. */ + start_addr = line; + end_addr = strchr(line, '-'); + file = strchr(line, '/'); + + /* More sanity check. */ + if (!(file > end_addr && end_addr != NULL && end_addr[0] == '-')) + continue; + + end_addr[0] = '\0'; + end_addr++; + end_addr_end = strchr(end_addr, ' '); + if (end_addr_end == NULL) + continue; + + end_addr_end[0] = '\0'; + len = strlen(file); + if (len == 0) + continue; + if (file[len - 1] == '\n') + file[len - 1] = '\0'; + + /* Get rid of "(deleted)" from the filename. */ + len = strlen(file); + if (len > 10 && strcmp(file + len - 10, " (deleted)") == 0) + file[len - 10] = '\0'; + + /* I don't know whether this can happen but better safe than sorry. */ + len = strlen(start_addr); + if (len != strlen(end_addr)) + continue; + + + /* Transform the addresses into a string in the form of 0xdeadbeef, + * then transform that into a pointer. */ + if (address_string_len < len + 3) { + address_string_len = len + 3; + address_string = + (char *) g_try_realloc(address_string, address_string_len); } - path = g_strdup (path); - g_free (line); - fclose (f); - return path; -} + memcpy(address_string, "0x", 2); + memcpy(address_string + 2, start_addr, len); + address_string[2 + len] = '\0'; + sscanf(address_string, "%p", &start_addr_p); + memcpy(address_string, "0x", 2); + memcpy(address_string + 2, end_addr, len); + address_string[2 + len] = '\0'; + sscanf(address_string, "%p", &end_addr_p); -/** @internal - * Find the canonical filename of the executable which owns symbol. - * Returns a filename which must be freed, or NULL on error. - */ -static char * -_br_find_exe_for_symbol (const void *symbol, GbrInitError *error) -{ - #define SIZE PATH_MAX + 100 - FILE *f; - size_t address_string_len; - char *address_string, line[SIZE], *found; - - if (symbol == NULL) - return (char *) NULL; - - f = fopen ("/proc/self/maps", "r"); - if (f == NULL) - return (char *) NULL; - - address_string_len = 4; - address_string = (char *) g_try_malloc (address_string_len); - found = (char *) NULL; - - while (!feof (f)) { - char *start_addr, *end_addr, *end_addr_end, *file; - void *start_addr_p, *end_addr_p; - size_t len; - - if (fgets (line, SIZE, f) == NULL) - break; - - /* Sanity check. */ - if (strstr (line, " r-xp ") == NULL || strchr (line, '/') == NULL) - continue; - - /* Parse line. */ - start_addr = line; - end_addr = strchr (line, '-'); - file = strchr (line, '/'); - - /* More sanity check. */ - if (!(file > end_addr && end_addr != NULL && end_addr[0] == '-')) - continue; - - end_addr[0] = '\0'; - end_addr++; - end_addr_end = strchr (end_addr, ' '); - if (end_addr_end == NULL) - continue; - - end_addr_end[0] = '\0'; - len = strlen (file); - if (len == 0) - continue; - if (file[len - 1] == '\n') - file[len - 1] = '\0'; - - /* Get rid of "(deleted)" from the filename. */ - len = strlen (file); - if (len > 10 && strcmp (file + len - 10, " (deleted)") == 0) - file[len - 10] = '\0'; - - /* I don't know whether this can happen but better safe than sorry. */ - len = strlen (start_addr); - if (len != strlen (end_addr)) - continue; - - - /* Transform the addresses into a string in the form of 0xdeadbeef, - * then transform that into a pointer. */ - if (address_string_len < len + 3) { - address_string_len = len + 3; - address_string = (char *) g_try_realloc (address_string, address_string_len); - } - - memcpy (address_string, "0x", 2); - memcpy (address_string + 2, start_addr, len); - address_string[2 + len] = '\0'; - sscanf (address_string, "%p", &start_addr_p); - - memcpy (address_string, "0x", 2); - memcpy (address_string + 2, end_addr, len); - address_string[2 + len] = '\0'; - sscanf (address_string, "%p", &end_addr_p); - - - if (symbol >= start_addr_p && symbol < end_addr_p) { - found = file; - break; - } + + if (symbol >= start_addr_p && symbol < end_addr_p) { + found = file; + break; } + } - g_free (address_string); - fclose (f); + g_free(address_string); + fclose(f); - if (found == NULL) - return (char *) NULL; - else - return g_strdup (found); + if (found == NULL) + return (char *) NULL; + else + return g_strdup(found); } static gchar *exe = NULL; -static void set_gerror (GError **error, GbrInitError errcode); +static void set_gerror(GError ** error, GbrInitError errcode); /** Initialize the BinReloc library (for applications). @@ -281,21 +279,20 @@ static void set_gerror (GError **error, GbrInitError errcode); * * @returns TRUE on success, FALSE if BinReloc failed to initialize. */ -gboolean -gbr_init (GError **error) +gboolean gbr_init(GError ** error) { - GbrInitError errcode = 0; - - /* Locate the application's filename. */ - exe = _br_find_exe (&errcode); - if (exe != NULL) - /* Success! */ - return TRUE; - else { - /* Failed :-( */ - set_gerror (error, errcode); - return FALSE; - } + GbrInitError errcode = 0; + + /* Locate the application's filename. */ + exe = _br_find_exe(&errcode); + if (exe != NULL) + /* Success! */ + return TRUE; + else { + /* Failed :-( */ + set_gerror(error, errcode); + return FALSE; + } } @@ -309,53 +306,51 @@ gbr_init (GError **error) * * @returns TRUE on success, FALSE if a filename cannot be found. */ -gboolean -gbr_init_lib (GError **error) +gboolean gbr_init_lib(GError ** error) { - GbrInitError errcode = 0; - - exe = _br_find_exe_for_symbol ((const void *) "", &errcode); - if (exe != NULL) - /* Success! */ - return TRUE; - else { - /* Failed :-( */ - set_gerror (error, errcode); - return exe != NULL; - } + GbrInitError errcode = 0; + + exe = _br_find_exe_for_symbol((const void *) "", &errcode); + if (exe != NULL) + /* Success! */ + return TRUE; + else { + /* Failed :-( */ + set_gerror(error, errcode); + return exe != NULL; + } } -static void -set_gerror (GError **error, GbrInitError errcode) +static void set_gerror(GError ** error, GbrInitError errcode) { - gchar *error_message; - - if (error == NULL) - return; - - switch (errcode) { - case GBR_INIT_ERROR_NOMEM: - error_message = "Cannot allocate memory."; - break; - case GBR_INIT_ERROR_OPEN_MAPS: - error_message = "Unable to open /proc/self/maps for reading."; - break; - case GBR_INIT_ERROR_READ_MAPS: - error_message = "Unable to read from /proc/self/maps."; - break; - case GBR_INIT_ERROR_INVALID_MAPS: - error_message = "The file format of /proc/self/maps is invalid."; - break; - case GBR_INIT_ERROR_DISABLED: - error_message = "Binary relocation support is disabled."; - break; - default: - error_message = "Unknown error."; - break; - }; - g_set_error (error, g_quark_from_static_string ("GBinReloc"), - errcode, "%s", error_message); + gchar *error_message; + + if (error == NULL) + return; + + switch (errcode) { + case GBR_INIT_ERROR_NOMEM: + error_message = "Cannot allocate memory."; + break; + case GBR_INIT_ERROR_OPEN_MAPS: + error_message = "Unable to open /proc/self/maps for reading."; + break; + case GBR_INIT_ERROR_READ_MAPS: + error_message = "Unable to read from /proc/self/maps."; + break; + case GBR_INIT_ERROR_INVALID_MAPS: + error_message = "The file format of /proc/self/maps is invalid."; + break; + case GBR_INIT_ERROR_DISABLED: + error_message = "Binary relocation support is disabled."; + break; + default: + error_message = "Unknown error."; + break; + }; + g_set_error(error, g_quark_from_static_string("GBinReloc"), + errcode, "%s", error_message); } @@ -368,17 +363,16 @@ set_gerror (GError **error, GbrInitError errcode) * then a copy of default_exe will be returned. If default_exe * is NULL, then NULL will be returned. */ -gchar * -gbr_find_exe (const gchar *default_exe) +gchar *gbr_find_exe(const gchar * default_exe) { - if (exe == NULL) { - /* BinReloc is not initialized. */ - if (default_exe != NULL) - return g_strdup (default_exe); - else - return NULL; - } - return g_strdup (exe); + if (exe == NULL) { + /* BinReloc is not initialized. */ + if (default_exe != NULL) + return g_strdup(default_exe); + else + return NULL; + } + return g_strdup(exe); } @@ -396,18 +390,17 @@ gbr_find_exe (const gchar *default_exe) * will be returned. If default_dir is NULL, then NULL will be * returned. */ -gchar * -gbr_find_exe_dir (const gchar *default_dir) +gchar *gbr_find_exe_dir(const gchar * default_dir) { - if (exe == NULL) { - /* BinReloc not initialized. */ - if (default_dir != NULL) - return g_strdup (default_dir); - else - return NULL; - } + if (exe == NULL) { + /* BinReloc not initialized. */ + if (default_dir != NULL) + return g_strdup(default_dir); + else + return NULL; + } - return g_path_get_dirname (exe); + return g_path_get_dirname(exe); } @@ -425,23 +418,22 @@ gbr_find_exe_dir (const gchar *default_dir) * will be returned. If default_prefix is NULL, then NULL will be * returned. */ -gchar * -gbr_find_prefix (const gchar *default_prefix) +gchar *gbr_find_prefix(const gchar * default_prefix) { - gchar *dir1, *dir2; - - if (exe == NULL) { - /* BinReloc not initialized. */ - if (default_prefix != NULL) - return g_strdup (default_prefix); - else - return NULL; - } + gchar *dir1, *dir2; - dir1 = g_path_get_dirname (exe); - dir2 = g_path_get_dirname (dir1); - g_free (dir1); - return dir2; + if (exe == NULL) { + /* BinReloc not initialized. */ + if (default_prefix != NULL) + return g_strdup(default_prefix); + else + return NULL; + } + + dir1 = g_path_get_dirname(exe); + dir2 = g_path_get_dirname(dir1); + g_free(dir1); + return dir2; } @@ -458,23 +450,22 @@ gbr_find_prefix (const gchar *default_prefix) * initialization function failed, then a copy of default_bin_dir will * be returned. If default_bin_dir is NULL, then NULL will be returned. */ -gchar * -gbr_find_bin_dir (const gchar *default_bin_dir) +gchar *gbr_find_bin_dir(const gchar * default_bin_dir) { - gchar *prefix, *dir; - - prefix = gbr_find_prefix (NULL); - if (prefix == NULL) { - /* BinReloc not initialized. */ - if (default_bin_dir != NULL) - return g_strdup (default_bin_dir); - else - return NULL; - } + gchar *prefix, *dir; + + prefix = gbr_find_prefix(NULL); + if (prefix == NULL) { + /* BinReloc not initialized. */ + if (default_bin_dir != NULL) + return g_strdup(default_bin_dir); + else + return NULL; + } - dir = g_build_filename (prefix, "bin", NULL); - g_free (prefix); - return dir; + dir = g_build_filename(prefix, "bin", NULL); + g_free(prefix); + return dir; } @@ -491,23 +482,22 @@ gbr_find_bin_dir (const gchar *default_bin_dir) * initialization function failed, then a copy of default_sbin_dir will * be returned. If default_bin_dir is NULL, then NULL will be returned. */ -gchar * -gbr_find_sbin_dir (const gchar *default_sbin_dir) +gchar *gbr_find_sbin_dir(const gchar * default_sbin_dir) { - gchar *prefix, *dir; - - prefix = gbr_find_prefix (NULL); - if (prefix == NULL) { - /* BinReloc not initialized. */ - if (default_sbin_dir != NULL) - return g_strdup (default_sbin_dir); - else - return NULL; - } + gchar *prefix, *dir; + + prefix = gbr_find_prefix(NULL); + if (prefix == NULL) { + /* BinReloc not initialized. */ + if (default_sbin_dir != NULL) + return g_strdup(default_sbin_dir); + else + return NULL; + } - dir = g_build_filename (prefix, "sbin", NULL); - g_free (prefix); - return dir; + dir = g_build_filename(prefix, "sbin", NULL); + g_free(prefix); + return dir; } @@ -525,23 +515,22 @@ gbr_find_sbin_dir (const gchar *default_sbin_dir) * will be returned. If default_data_dir is NULL, then NULL will be * returned. */ -gchar * -gbr_find_data_dir (const gchar *default_data_dir) +gchar *gbr_find_data_dir(const gchar * default_data_dir) { - gchar *prefix, *dir; - - prefix = gbr_find_prefix (NULL); - if (prefix == NULL) { - /* BinReloc not initialized. */ - if (default_data_dir != NULL) - return g_strdup (default_data_dir); - else - return NULL; - } + gchar *prefix, *dir; + + prefix = gbr_find_prefix(NULL); + if (prefix == NULL) { + /* BinReloc not initialized. */ + if (default_data_dir != NULL) + return g_strdup(default_data_dir); + else + return NULL; + } - dir = g_build_filename (prefix, "share", NULL); - g_free (prefix); - return dir; + dir = g_build_filename(prefix, "share", NULL); + g_free(prefix); + return dir; } @@ -558,23 +547,22 @@ gbr_find_data_dir (const gchar *default_data_dir) * initialization function failed, then a copy of default_locale_dir will be returned. * If default_locale_dir is NULL, then NULL will be returned. */ -gchar * -gbr_find_locale_dir (const gchar *default_locale_dir) +gchar *gbr_find_locale_dir(const gchar * default_locale_dir) { - gchar *data_dir, *dir; - - data_dir = gbr_find_data_dir (NULL); - if (data_dir == NULL) { - /* BinReloc not initialized. */ - if (default_locale_dir != NULL) - return g_strdup (default_locale_dir); - else - return NULL; - } + gchar *data_dir, *dir; + + data_dir = gbr_find_data_dir(NULL); + if (data_dir == NULL) { + /* BinReloc not initialized. */ + if (default_locale_dir != NULL) + return g_strdup(default_locale_dir); + else + return NULL; + } - dir = g_build_filename (data_dir, "locale", NULL); - g_free (data_dir); - return dir; + dir = g_build_filename(data_dir, "locale", NULL); + g_free(data_dir); + return dir; } @@ -591,23 +579,22 @@ gbr_find_locale_dir (const gchar *default_locale_dir) * initialization function failed, then a copy of default_lib_dir will be returned. * If default_lib_dir is NULL, then NULL will be returned. */ -gchar * -gbr_find_lib_dir (const gchar *default_lib_dir) +gchar *gbr_find_lib_dir(const gchar * default_lib_dir) { - gchar *prefix, *dir; - - prefix = gbr_find_prefix (NULL); - if (prefix == NULL) { - /* BinReloc not initialized. */ - if (default_lib_dir != NULL) - return g_strdup (default_lib_dir); - else - return NULL; - } + gchar *prefix, *dir; - dir = g_build_filename (prefix, "lib", NULL); - g_free (prefix); - return dir; + prefix = gbr_find_prefix(NULL); + if (prefix == NULL) { + /* BinReloc not initialized. */ + if (default_lib_dir != NULL) + return g_strdup(default_lib_dir); + else + return NULL; + } + + dir = g_build_filename(prefix, "lib", NULL); + g_free(prefix); + return dir; } @@ -624,23 +611,22 @@ gbr_find_lib_dir (const gchar *default_lib_dir) * function failed, then a copy of default_libexec_dir will be returned. * If default_libexec_dir is NULL, then NULL will be returned. */ -gchar * -gbr_find_libexec_dir (const gchar *default_libexec_dir) +gchar *gbr_find_libexec_dir(const gchar * default_libexec_dir) { - gchar *prefix, *dir; - - prefix = gbr_find_prefix (NULL); - if (prefix == NULL) { - /* BinReloc not initialized. */ - if (default_libexec_dir != NULL) - return g_strdup (default_libexec_dir); - else - return NULL; - } + gchar *prefix, *dir; - dir = g_build_filename (prefix, "libexec", NULL); - g_free (prefix); - return dir; + prefix = gbr_find_prefix(NULL); + if (prefix == NULL) { + /* BinReloc not initialized. */ + if (default_libexec_dir != NULL) + return g_strdup(default_libexec_dir); + else + return NULL; + } + + dir = g_build_filename(prefix, "libexec", NULL); + g_free(prefix); + return dir; } @@ -657,26 +643,24 @@ gbr_find_libexec_dir (const gchar *default_libexec_dir) * function failed, then a copy of default_etc_dir will be returned. * If default_etc_dir is NULL, then NULL will be returned. */ -gchar * -gbr_find_etc_dir (const gchar *default_etc_dir) +gchar *gbr_find_etc_dir(const gchar * default_etc_dir) { - gchar *prefix, *dir; - - prefix = gbr_find_prefix (NULL); - if (prefix == NULL) { - /* BinReloc not initialized. */ - if (default_etc_dir != NULL) - return g_strdup (default_etc_dir); - else - return NULL; - } + gchar *prefix, *dir; - dir = g_build_filename (prefix, "etc", NULL); - g_free (prefix); - return dir; + prefix = gbr_find_prefix(NULL); + if (prefix == NULL) { + /* BinReloc not initialized. */ + if (default_etc_dir != NULL) + return g_strdup(default_etc_dir); + else + return NULL; + } + + dir = g_build_filename(prefix, "etc", NULL); + g_free(prefix); + return dir; } G_END_DECLS - -#endif /* __BINRELOC_C__ */ +#endif /* __BINRELOC_C__ */ |