Jacko Jacko - 7 months ago 27
Bash Question

Finding a specific machine code for objcopy

I need to run the following two commands on each file in a directory:

A) First Command:

readelf -aW A_FILE | grep Machine


where
A_FILE
is the name of a particular file.

The output from the first command looks something like this:

Machine: <unknown>: 0xXXX


where
0xXXX
is some hex number.

B) Second command

objcopy -I elf64-x86-64 -O elf64-x86-64 -R .source -R .llvmir -R .amdil --alt-machine-code=<Machine> A_FILE A_FILE.STRIPPED


where
<Machine>
is the hex number from the first command, and
A_FILE.STRIPPED
is the name of the output file from
objcopy
. (
STRIPPED
is arbitrary, could be any piece of text)

Edit: here is the final script I ended up with, thanks to Charles, after a little debugging:

#!/bin/bash
# ^^^^- important, not /bin/sh

# define a regex, in ERE form, to extract the content you want in a match group
re='^.*machine.*(0x...).*'


# iterate over files, putting each in $f
for f in *; do

# don't operate on files we previously generated
[[ $f = *.stripped ]] && continue

printf "\n\n\nProcessing file: $f \n\n"
# actually run readelf, taking first line matching Machine
# on any other platform but Cygwin I would write it more like this:
# read -r m_line < <(readelf -aW "$f" | grep -m 1 machine)
# ...but on cygwin, <() is buggy, so it gets written like this:
m_line=$(readelf -aW "$f" | grep machine)

printf "readelf output: $m_line \n\n"

[[ $m_line =~ $re ]] || continue # check whether we match the regex

# if we get here, the regex matched; copy the first match group into a variable
code=${BASH_REMATCH[1]}

printf "Found machine code $code\n\n"

# ...and use that variable in calling objcopy
objcopy -I elf64-x86-64 -O elf64-x86-64 -R .source -R .llvmir -R .amdil \
--alt-machine-code="$code" \
"$f" "$f.stripped"

printf "\nProcessing complete\n\n"
done

Answer
#!/bin/bash
#      ^^^^- important, not /bin/sh

# define a regex, in ERE form, to extract the content you want in a match group
re='^.*machine.*(0x...).*'

# iterate over files, putting each in $f
for f in *; do

  # don't operate on files we previously generated
  [[ $f = *.stripped ]] && continue

  # actually run readelf, taking first line matching Machine
  # on any other platform but Cygwin I would write it more like this:
  #   read -r m_line < <(readelf -aW "$f" | egrep -m 1 '^machine:')
  # ...but on cygwin, <() is buggy, so it gets written like this:
  m_line=$(readelf -aW "$f" | egrep -m 1 '^machine:')

  [[ $m_line =~ $re ]] || continue # check whether we match the regex

  # if we get here, the regex matched; copy the first match group into a variable
  code=${BASH_REMATCH[1]}

  # ...and use that variable in calling objcopy
  objcopy -I elf64-x86-64  -O elf64-x86-64 -R .source -R .llvmir -R .amdil \
    --alt-machine-code="$code" \
    "$f" "$f.stripped"
done
Comments