// Usage: // // dlsym $RTLD_DEFAULT "errno" // static int get_symbol_address(WORD_LIST *list) { int opt; void *handle; void *symbol; char *resultname; char retval[256]; resultname = "DLRETVAL"; reset_internal_getopt(); // $ dlcall [-n name] while ((opt = internal_getopt(list, "n:")) != -1) { switch (opt) { case 'n': resultname = list_optarg; break; default: builtin_usage(); return EX_USAGE; } } // Skip past any options. if ((list = loptend) == NULL || list->next == NULL) { builtin_usage(); return EX_USAGE; } if (check_parse_ulong(list->word->word, (void *) &handle) == 0) { builtin_warning("handle %s %p is not well-formed", list->word->word, handle); return EX_USAGE; } if (!(symbol = dlsym(handle, list->next->word->word))) { builtin_warning("failed to resolve symbol %s, %s", list->next->word->word, dlerror()); return EXECUTION_FAILURE; } snprintf(retval, sizeof retval, "pointer:%p", symbol); fprintf(stderr, "%s\n", retval); bind_variable(resultname, retval, 0); return EXECUTION_SUCCESS; }
static int close_dynamic_library(WORD_LIST *list) { void *handle; if (!list) { builtin_usage(); return EX_USAGE; } while (list) { if (!check_parse_ulong(list->word->word, (long *) &handle)) { builtin_warning("could not parse handle identifier %s", list->word->word); } else { if (dlclose(handle) != 0) { builtin_warning("dlclose set an error for %s, %s", list->word->word, dlerror()); } } list = list->next; } return 0; }
bool decode_primitive_type(const char *parameter, void **value, ffi_type **type) { const char *prefix; prefix = NULL; *value = NULL; *type = NULL; // If a colon exists, then everything before it is a type if (strchr(parameter, ':')) { // Extract the two components. prefix = strndupa(parameter, strchr(parameter, ':') - parameter); parameter = strchr(parameter, ':') + 1; } else { intmax_t n; char *string; // No type was specified, so there are only two possibilities, // If this is a legal number, then it's an int. Otherwise, this is a // string. if (check_parse_long(parameter, &n)) { *type = &ffi_type_sint; *value = malloc(ffi_type_sint.size); memcpy(*value, &n, ffi_type_sint.size); return true; } // This must be a string. *type = &ffi_type_pointer; *value = malloc(ffi_type_pointer.size); string = strdup(parameter); memcpy(*value, &string, ffi_type_pointer.size); return true; } if (decode_type_prefix(prefix, parameter, type, value, NULL) != true) { builtin_warning("parameter decoding failed"); return false; } return true; }
// Return the value of the single rtld flag specified. static uint32_t rtld_flags_decode(const char *flag) { intmax_t result; // Enumerate through all flags to find the one specified, this is // suboptimal but there are only 32 possible flags. for (uint32_t i = 0; i < 31; i++) { if (strcmp(rtld_flags_encode(1 << i), flag) == 0) { return 1 << i; } } // Perhaps it was specified numerically? if (check_parse_ulong(flag, &result)) { return result; } builtin_warning("invalid or unrecognised rtld flag ignored: %s", flag); return 0; }
// Usage: // // dlcall $RTLD_DEFAULT "printf" "hello %s %u %c" $USER 123 int:10 // static int call_foreign_function(WORD_LIST *list) { unsigned nargs; int opt; ffi_cif cif; ffi_type **argtypes; ffi_type *rettype; void **values; void *handle; void *func; char *prefix; char *format; char *resultname; nargs = 0; argtypes = NULL; values = NULL; format = NULL; prefix = NULL; rettype = &ffi_type_void; resultname = "DLRETVAL"; reset_internal_getopt(); // $ dlcall [-a abi] [-r type] [-n name] while ((opt = internal_getopt(list, "a:r:n:")) != -1) { switch (opt) { case 'a': builtin_warning("FIXME: only abi %u is currently supported", FFI_DEFAULT_ABI); return 1; break; case 'r': if (decode_type_prefix(prefix = list_optarg, NULL, &rettype, NULL, &format) != true) { builtin_warning("failed to parse return type"); return 1; } break; case 'n': resultname = list_optarg; break; default: builtin_usage(); return 1; } } // Skip past any options. if ((list = loptend) == NULL || list->next == NULL) { builtin_usage(); return 1; } if (check_parse_ulong(list->word->word, (void *) &handle) == 0) { builtin_warning("handle %s %p is not well-formed", list->word->word, handle); return 1; } if (!(func = dlsym(handle, list->next->word->word))) { builtin_warning("failed to resolve symbol %s, %s", list->next->word->word, dlerror()); return 1; } // Skip to optional parameters list = list->next->next; while (list) { argtypes = realloc(argtypes, (nargs + 1) * sizeof(ffi_type *)); values = realloc(values, (nargs + 1) * sizeof(void *)); if (decode_primitive_type(list->word->word, &values[nargs], &argtypes[nargs]) != true) { builtin_error("failed to decode type from parameter %s", list->word->word); goto error; } nargs++; list = list->next; } if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, nargs, rettype, argtypes) == FFI_OK) { char *retval; void *rc = alloca(rettype->size); // Do the call. ffi_call(&cif, func, rc, values); // Print the result. if (format) { switch (rettype->size) { case 1: asprintf(&retval, format, *(uint8_t *) rc); break; case 2: asprintf(&retval, format, *(uint16_t *) rc); break; case 4: asprintf(&retval, format, *(uint32_t *) rc, *(float *) rc); break; case 8: asprintf(&retval, format, *(uint64_t *) rc, *(double *) rc); break; case 16: asprintf(&retval, format, *(long double *) rc); break; default: builtin_error("cannot handle size %lu", rettype->size); abort(); } fprintf(stderr, "%s\n", retval); bind_variable(resultname, retval, 0); free(retval); } } for (unsigned i = 0; i < nargs; i++) free(values[i]); free(values); free(argtypes); return 0; error: for (unsigned i = 0; i < nargs; i++) free(values[i]); free(values); free(argtypes); return 1; }
bool decode_type_prefix(const char *prefix, const char *value, ffi_type **type, void **result, char **pformat) { static struct { char *prefix; ffi_type *type; char *sformat; char *pformat; } types[] = { { "uint8", &ffi_type_uint8, "%" SCNu8, "uint8:%" PRIu8 }, { "int8", &ffi_type_sint8, "%" SCNd8, "int8:%" PRId8 }, { "uint16", &ffi_type_uint16, "%" SCNu16, "uint16:%" PRIu16 }, { "int16", &ffi_type_sint16, "%" SCNd16, "int16:%" PRId16 }, { "uint32", &ffi_type_uint32, "%" SCNu32, "uint32:%" PRIu32 }, { "int32", &ffi_type_sint32, "%" SCNd32, "int32:%" PRId32 }, { "uint64", &ffi_type_uint64, "%" SCNu64, "uint64:%" PRIu64 }, { "int64", &ffi_type_sint64, "%" SCNd64, "int64:%" PRId64 }, { "float", &ffi_type_float, "%f", "float:%f" }, { "double", &ffi_type_double, "%lf", "double:%lf" }, { "char", &ffi_type_schar, "%c", "char:%c" }, { "uchar", &ffi_type_uchar, "%c", "uchar:%c" }, { "ushort", &ffi_type_ushort, "%hu", "ushort:%hu" }, { "short", &ffi_type_sshort, "%hd", "short:%hd", }, { "unsigned", &ffi_type_uint, "%u", "unsigned:%u" }, { "int", &ffi_type_sint, "%d", "int:%d" }, { "ulong", &ffi_type_ulong, "%lu", "ulong:%lu" }, { "long", &ffi_type_slong, "%ld", "long:%ld" }, { "longdouble", &ffi_type_longdouble, "%llg", "longdouble:%llg" }, { "pointer", &ffi_type_pointer, "%p", "pointer:%p" }, { "string", &ffi_type_pointer, "%ms", "string:%s" }, { "void", &ffi_type_void, "", "" }, { NULL }, }; for (int i = 0; types[i].prefix; i++) { if (strcmp(types[i].prefix, prefix) == 0) { // Prefix matched type, return information user requested. if (type) { *type = types[i].type; } if (pformat) { *pformat = types[i].pformat; } // Caller wants us to decode it, lets go ahead. if (result) { *result = malloc(types[i].type->size); if (sscanf(value, types[i].sformat, *result) != 1) { builtin_warning("failed to parse %s as a %s", value, prefix); free(*result); return false; } } return true; } } builtin_warning("unrecognised type prefix %s", prefix); return false; }
// Usage: // // dlcall "printf" "hello %s %u %c" $USER 123 int:10 // static int call_foreign_function(WORD_LIST *list) { unsigned nargs; unsigned i; int opt; ffi_cif cif; ffi_type **argtypes; ffi_type *rettype; void **values; void *handle; void *func; char *prefix; char *format; char *resultname; nargs = 0; argtypes = NULL; values = NULL; format = NULL; prefix = NULL; rettype = &ffi_type_void; resultname = "DLRETVAL"; handle = RTLD_DEFAULT; reset_internal_getopt(); // $ dlcall [-a abi] [-r type] [-n name] [-h handle] symbol args... while ((opt = internal_getopt(list, "h:a:r:n:")) != -1) { switch (opt) { case 'a': builtin_warning("FIXME: only abi %u is currently supported", FFI_DEFAULT_ABI); return 1; break; case 'r': if (decode_type_prefix(prefix = list_optarg, NULL, &rettype, NULL, &format) != true) { builtin_warning("failed to parse return type"); return 1; } break; case 'n': resultname = list_optarg; break; case 'h': if (check_parse_ulong(list_optarg, (void *) &handle) == 0) { builtin_warning("handle %s %p is not well-formed", list_optarg, handle); return EXECUTION_FAILURE; } break; default: builtin_usage(); return EX_USAGE; } } // Skip past any options. if ((list = loptend) == NULL) { builtin_usage(); return EX_USAGE; } if (!(func = dlsym(handle, list->word->word))) { builtin_warning("failed to resolve symbol %s, %s", list->word->word, dlerror()); return 1; } // Skip to optional parameters list = list->next; while (list) { argtypes = realloc(argtypes, (nargs + 1) * sizeof(ffi_type *)); values = realloc(values, (nargs + 1) * sizeof(void *)); if (decode_primitive_type(list->word->word, &values[nargs], &argtypes[nargs]) != true) { builtin_error("failed to decode type from parameter %s", list->word->word); goto error; } nargs++; list = list->next; } if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, nargs, rettype, argtypes) == FFI_OK) { char *retval; void *rc = alloca(rettype->size); // Do the call. ffi_call(&cif, func, rc, values); // Decode the result. if (format) { retval = encode_primitive_type(format, rettype, rc); // If this is an interactive shell, print the output. if (interactive_shell) { fprintf(stderr, "%s\n", retval); } // Save the result to the requested location. bind_variable(resultname, retval, 0); // Bash maintains it's own copy of this string, so we can throw it away. free(retval); } } for (i = 0; i < nargs; i++) free(values[i]); free(values); free(argtypes); return 0; error: for (i = 0; i < nargs; i++) free(values[i]); free(values); free(argtypes); return 1; }
// Usage: // // dlsym $RTLD_DEFAULT "errno" // static int get_symbol_address(WORD_LIST *list) { int opt; void *handle; void *symbol; char *resultname; char *format; char *retval; ffi_type *rettype; handle = RTLD_DEFAULT; resultname = "DLRETVAL"; rettype = NULL; reset_internal_getopt(); // $ dlsym [-n name] [-h handle] symbol while ((opt = internal_getopt(list, "d:h:n:")) != -1) { switch (opt) { case 'd': if (decode_type_prefix(list_optarg, NULL, &rettype, NULL, &format) != true) { builtin_warning("failed to parse dereference type"); return 1; } break; case 'n': resultname = list_optarg; break; case 'h': if (check_parse_ulong(list_optarg, (void *) &handle) == 0) { builtin_warning("handle %s %p is not well-formed", list_optarg, handle); return EXECUTION_FAILURE; } break; default: builtin_usage(); return EX_USAGE; } } // Skip past any options. if ((list = loptend) == NULL) { builtin_usage(); return EX_USAGE; } if (!(symbol = dlsym(handle, list->word->word))) { builtin_warning("failed to resolve symbol %s, %s", list->word->word, dlerror()); return EXECUTION_FAILURE; } if (rettype == NULL) { asprintf(&retval, "pointer:%p", symbol); } else { retval = encode_primitive_type(format, rettype, symbol); } if (interactive_shell) { fprintf(stderr, "%s\n", retval); } bind_variable(resultname, retval, 0); free(retval); return EXECUTION_SUCCESS; }
// Usage: // // dlopen [-N] [-t] [-d] [-g] [-n] [library] [RTLD_NODELETE|RTLD_GLOBAL|...] [...] // static int open_dynamic_library(WORD_LIST *list) { char varname[1024]; char value[1024]; uint32_t flags; void *handle; int opt; reset_internal_getopt(); flags = RTLD_LAZY | RTLD_GLOBAL; handle = NULL; // Options can either be specified as bash-like flags, or as a list. The // bash-like flags look like this: // // $ dlopen -tg libc.so // while ((opt = internal_getopt(list, "lNtdgn")) != -1) { switch (opt) { // RTLD_LAZY and RTLD_NOW are mutually exclusive. case 'l': flags = (flags & ~RTLD_NOW) | RTLD_LAZY; break; case 'N': flags = (flags & ~RTLD_LAZY) | RTLD_NOW; break; case 't': flags |= RTLD_NOLOAD; break; case 'd': #ifdef RTLD_DEEPBIND flags |= RTLD_DEEPBIND; #else builtin_warning("RTLD_DEEPBIND is not supported on this platform"); #endif break; case 'g': flags &= ~RTLD_GLOBAL; break; case 'n': flags |= RTLD_NODELETE; break; default: builtin_usage(); return EX_USAGE; } } // Skip past any options. if ((list = loptend) == NULL) { builtin_usage(); return 1; } // Check and decode parameters, which can be specified as strings. // // $ dlopen libc.so RTLD_LAZY RTLD_NODELETE // // or, as an integer // // $ dlopen libc.so 0x10101 // if (list->next) { WORD_LIST *flaglist = list->next; // Caller wants more control over flags, so reset and decode the flags // specified. for (flags = 0; flaglist; flaglist = flaglist->next) { flags |= rtld_flags_decode(flaglist->word->word); } } // Now list->word is the library name. if (!(handle = dlopen(list->word->word, flags))) { builtin_error("dlopen(\"%s\", %#x) failed, %s", list->word->word, flags, dlerror()); return 1; } // Print the handle. if (interactive_shell) { printf("%p\n", handle); } snprintf(varname, sizeof varname, "DLHANDLES[\"%s\"]", basename(list->word->word)); snprintf(value, sizeof value, "%p", handle); // Make the handle available programmatically. if (assign_array_element(varname, value, AV_USEIND) == NULL) { builtin_error("failed to append element to $DLHANDLES array"); dlclose(handle); return 1; } return 0; }
static int generate_native_callback(WORD_LIST *list) { int nargs; void *callback; ffi_cif *cif; ffi_closure *closure; ffi_type **argtypes; ffi_type *rettype; char **proto; char *resultname = "DLRETVAL"; char opt; reset_internal_getopt(); // $ dlcall [-a abi] [-r type] [-n name] while ((opt = internal_getopt(list, "a:r:n:")) != -1) { switch (opt) { case 'n': resultname = list_optarg; break; default: builtin_usage(); return EX_USAGE; } } // Skip past any options. if ((list = loptend) == NULL || !list->next) { builtin_usage(); return EX_USAGE; } closure = ffi_closure_alloc(sizeof(ffi_closure), &callback); cif = malloc(sizeof(ffi_cif)); argtypes = NULL; proto = malloc(sizeof(char *)); proto[0] = strdup(list->word->word); nargs = 0; list = list->next; // Second parameter must be the return type if (decode_type_prefix(list->word->word, NULL, &rettype, NULL, NULL) != true) { builtin_warning("couldnt parse the return type %s", list->word->word); return EXECUTION_FAILURE; } // Skip past return type list = list->next; while (list) { argtypes = realloc(argtypes, (nargs + 1) * sizeof(ffi_type *)); proto = realloc(proto, (nargs + 1 + 1) * sizeof(char *)); if (decode_type_prefix(list->word->word, NULL, &argtypes[nargs], NULL, &proto[nargs+1]) != true) { builtin_error("failed to decode type from parameter %s", list->word->word); goto error; } list = list->next; nargs++; } if (ffi_prep_cif(cif, FFI_DEFAULT_ABI, nargs, rettype, argtypes) == FFI_OK) { // Initialize the closure. if (ffi_prep_closure_loc(closure, cif, execute_bash_trampoline, proto, callback) == FFI_OK) { char retval[1024]; snprintf(retval, sizeof retval, "pointer:%p", callback); fprintf(stderr, "%s\n", retval); bind_variable(resultname, retval, 0); } } //free(argtypes); return 0; error: //free(argtypes); return 1; }