#!/bin/sh # # Shell command language linker # # Copyright (C) 2015, 2019 Patrick "P. J." McDermott # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . set -u VERSION='0.3.0' die() { local fmt="${1}" shift 1 printf "shld: ${fmt}\n" "${@}" >&2 exit 2 } link() { local make_executable="${1}" local interpreter="${2}" local entry_point="${3}" local libs="${4}" local output="${5}" shift 5 local input= # Open output file. if ! exec 3>"${output}~"; then die 'Cannot open file "%s"' "${output}~" fi # Write magic number and interpreter path. if ${make_executable}; then printf '#!%s\n' "${interpreter}" >&3 fi # Write __init() function. cat >&3 <<-'EOF' __init_funcs='' __init() { __init_funcs="${__init_funcs} ${1}" } EOF # Read input files. for input in "${@}"; do if ! cat "${input}" >&3; then die 'Cannot read file "%s"' "${input}" fi done # Write __DT_NEEDED list and basic RTLD. printf "__DT_NEEDED='%s'\n" "${libs}" >&3 cat >&3 <<-'EOF' : ${SHLD_LIBRARY_PATH:=${LIBDATADIR}} for __lib in ${__DT_NEEDED}; do __found=false IFS=':' for __el in ${SHLD_LIBRARY_PATH}; do if __f="$(cat "${__el}/${__lib}" 2>/dev/null)" then eval "${__f}" __found=true break fi done unset IFS if ! ${__found}; then printf "%s: error while loading shell $(: \ )shared libraries: %s: cannot open $(: \ )shell shared object file\n" \ "${0}" "${__lib}" >&2 exit 127 fi done unset __lib __el __found __f EOF # Add call to init functions. cat >&3 <<-'EOF' unset IFS for __func in ${__init_funcs}; do ${__func} done EOF # Add call to entry point. if ${make_executable}; then printf '%s "${@}"\n' "${entry_point}" >&3 fi # Close output file, make it executable, and set its name. exec 3>&- if ${make_executable}; then if ! chmod a+x "${output}~"; then die 'Cannot set mode of file "%s"' "${output}~" fi fi if ! mv "${output}~" "${output}"; then die 'Cannot rename file to "%s"' "${output}" fi } usage() { printf 'Usage: %s [option ...] ...\n' "${0}" } help() { usage cat < Add to the list of libraries to load at run time -I Make an executable and use as the interpreter for your program, instead of the default of "/bin/sh" -e Make an executable and use as the function for beginning execution of your program, instead of the default of "main" -o Use as the name of the program produced by shld, instead of the default of "out.sh" EOF } version() { cat <. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. EOF } main() { local opt= local libs='' local make_executable=false local interpreter='/bin/sh' local entry_point='main' local output='out.sh' while getopts 'hVl:I:e:o:' opt; do case "${opt}" in 'h') help exit ;; 'V') version exit ;; 'l') libs="${libs}${OPTARG} " ;; 'I') interpreter="${OPTARG}" make_executable=true ;; 'e') entry_point="${OPTARG}" make_executable=true ;; 'o') output="${OPTARG}" ;; esac done shift $(($OPTIND - 1)) if [ ${#} -lt 1 ]; then usage >&2 exit 1 fi link "${make_executable}" "${interpreter}" "${entry_point}" "${libs}" \ "${output}" "${@}" } main "${@}"