chrpath.bbclass 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. #
  2. # Copyright OpenEmbedded Contributors
  3. #
  4. # SPDX-License-Identifier: MIT
  5. #
  6. CHRPATH_BIN ?= "chrpath"
  7. PREPROCESS_RELOCATE_DIRS ?= ""
  8. def process_file_linux(cmd, fpath, rootdir, baseprefix, tmpdir, d, break_hardlinks = False):
  9. import subprocess, oe.qa
  10. with oe.qa.ELFFile(fpath) as elf:
  11. try:
  12. elf.open()
  13. except oe.qa.NotELFFileError:
  14. return
  15. try:
  16. out = subprocess.check_output([cmd, "-l", fpath], universal_newlines=True)
  17. except subprocess.CalledProcessError:
  18. return
  19. # Handle RUNPATH as well as RPATH
  20. out = out.replace("RUNPATH=","RPATH=")
  21. # Throw away everything other than the rpath list
  22. curr_rpath = out.partition("RPATH=")[2]
  23. #bb.note("Current rpath for %s is %s" % (fpath, curr_rpath.strip()))
  24. rpaths = curr_rpath.strip().split(":")
  25. new_rpaths = []
  26. modified = False
  27. for rpath in rpaths:
  28. # If rpath is already dynamic copy it to new_rpath and continue
  29. if rpath.find("$ORIGIN") != -1:
  30. new_rpaths.append(rpath)
  31. continue
  32. rpath = os.path.normpath(rpath)
  33. if baseprefix not in rpath and tmpdir not in rpath:
  34. # Skip standard search paths
  35. if rpath in ['/lib', '/usr/lib', '/lib64/', '/usr/lib64']:
  36. bb.warn("Skipping RPATH %s as is a standard search path for %s" % (rpath, fpath))
  37. modified = True
  38. continue
  39. new_rpaths.append(rpath)
  40. continue
  41. new_rpaths.append("$ORIGIN/" + os.path.relpath(rpath, os.path.dirname(fpath.replace(rootdir, "/"))))
  42. modified = True
  43. # if we have modified some rpaths call chrpath to update the binary
  44. if modified:
  45. if break_hardlinks:
  46. bb.utils.break_hardlinks(fpath)
  47. args = ":".join(new_rpaths)
  48. #bb.note("Setting rpath for %s to %s" %(fpath, args))
  49. try:
  50. subprocess.check_output([cmd, "-r", args, fpath],
  51. stderr=subprocess.PIPE, universal_newlines=True)
  52. except subprocess.CalledProcessError as e:
  53. bb.fatal("chrpath command failed with exit code %d:\n%s\n%s" % (e.returncode, e.stdout, e.stderr))
  54. def process_file_darwin(cmd, fpath, rootdir, baseprefix, tmpdir, d, break_hardlinks = False):
  55. import subprocess as sub
  56. p = sub.Popen([d.expand("${HOST_PREFIX}otool"), '-L', fpath],stdout=sub.PIPE,stderr=sub.PIPE, text=True)
  57. out, err = p.communicate()
  58. # If returned successfully, process stdout for results
  59. if p.returncode != 0:
  60. return
  61. for l in out.split("\n"):
  62. if "(compatibility" not in l:
  63. continue
  64. rpath = l.partition("(compatibility")[0].strip()
  65. if baseprefix not in rpath:
  66. continue
  67. if break_hardlinks:
  68. bb.utils.break_hardlinks(fpath)
  69. newpath = "@loader_path/" + os.path.relpath(rpath, os.path.dirname(fpath.replace(rootdir, "/")))
  70. p = sub.Popen([d.expand("${HOST_PREFIX}install_name_tool"), '-change', rpath, newpath, fpath],stdout=sub.PIPE,stderr=sub.PIPE)
  71. out, err = p.communicate()
  72. def process_dir(rootdir, directory, d, break_hardlinks = False):
  73. bb.debug(2, "Checking %s for binaries to process" % directory)
  74. if not os.path.exists(directory):
  75. return
  76. import stat
  77. rootdir = os.path.normpath(rootdir)
  78. cmd = d.expand('${CHRPATH_BIN}')
  79. tmpdir = os.path.normpath(d.getVar('TMPDIR', False))
  80. baseprefix = os.path.normpath(d.expand('${base_prefix}'))
  81. hostos = d.getVar("HOST_OS")
  82. if "linux" in hostos:
  83. process_file = process_file_linux
  84. elif "darwin" in hostos:
  85. process_file = process_file_darwin
  86. else:
  87. # Relocations not supported
  88. return
  89. dirs = os.listdir(directory)
  90. for file in dirs:
  91. fpath = directory + "/" + file
  92. fpath = os.path.normpath(fpath)
  93. if os.path.islink(fpath):
  94. # Skip symlinks
  95. continue
  96. if os.path.isdir(fpath):
  97. process_dir(rootdir, fpath, d, break_hardlinks = break_hardlinks)
  98. else:
  99. #bb.note("Testing %s for relocatability" % fpath)
  100. # We need read and write permissions for chrpath, if we don't have
  101. # them then set them temporarily. Take a copy of the files
  102. # permissions so that we can restore them afterwards.
  103. perms = os.stat(fpath)[stat.ST_MODE]
  104. if os.access(fpath, os.W_OK|os.R_OK):
  105. perms = None
  106. else:
  107. # Temporarily make the file writeable so we can chrpath it
  108. os.chmod(fpath, perms|stat.S_IRWXU)
  109. process_file(cmd, fpath, rootdir, baseprefix, tmpdir, d, break_hardlinks = break_hardlinks)
  110. if perms:
  111. os.chmod(fpath, perms)
  112. def rpath_replace (path, d):
  113. bindirs = d.expand("${bindir} ${sbindir} ${base_sbindir} ${base_bindir} ${libdir} ${base_libdir} ${libexecdir} ${PREPROCESS_RELOCATE_DIRS}").split()
  114. for bindir in bindirs:
  115. #bb.note ("Processing directory " + bindir)
  116. directory = path + "/" + bindir
  117. process_dir (path, directory, d)