Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve rpath #5400

Merged
merged 3 commits into from
Jul 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion xmake/modules/core/tools/gcc.lua
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,10 @@ end
-- make the rpathdir flag
function nf_rpathdir(self, dir, opt)
opt = opt or {}
local extra = opt.extra
if extra and extra.installonly then
return
end
dir = path.translate(dir)
if self:has_flags("-Wl,-rpath=" .. dir, "ldflags") then
local flags = {"-Wl,-rpath=" .. (dir:gsub("@[%w_]+", function (name)
Expand All @@ -395,7 +399,6 @@ function nf_rpathdir(self, dir, opt)
end))}
-- add_rpathdirs("...", {runpath = false})
-- https://github.com/xmake-io/xmake/issues/5109
local extra = opt.extra
if extra then
if extra.runpath == false and self:has_flags("-Wl,-rpath=" .. dir .. ",--disable-new-dtags", "ldflags") then
flags[1] = flags[1] .. ",--disable-new-dtags"
Expand Down
167 changes: 147 additions & 20 deletions xmake/modules/utils/binary/rpath.lua
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ function _get_rpath_list_by_objdump(binaryfile, opt)
local p = line:split("%s+")[2]
if p then
list = list or {}
table.insert(list, p:trim())
table.join2(list, path.splitenv(p:trim()))
end
end
end
Expand Down Expand Up @@ -109,13 +109,13 @@ function _get_rpath_list_by_readelf(binaryfile, opt)
local p = line:match("Library runpath: %[(.-)%]")
if p then
list = list or {}
table.insert(list, p:trim())
table.join2(list, path.splitenv(p:trim()))
end
elseif line:find("RPATH", 1, true) then
local p = line:match("Library rpath: %[(.-)%]")
if p then
list = list or {}
table.insert(list, p:trim())
table.join2(list, path.splitenv(p:trim()))
end
end
end
Expand All @@ -124,6 +124,31 @@ function _get_rpath_list_by_readelf(binaryfile, opt)
return list
end

-- $ patchelf --print-rpath ./build/linux/x86_64/release/app
-- $ORIGIN:xxx:bar
function _get_rpath_list_by_patchelf(binaryfile, opt)
local plat = opt.plat or os.host()
local arch = opt.arch or os.arch()
if plat ~= "linux" and plat ~= "bsd" and plat ~= "android" and plat ~= "cross" then
return
end
local list
local cachekey = "utils.binary.rpath"
local patchelf = find_tool("patchelf", {cachekey = cachekey})
if patchelf then
local binarydir = path.directory(binaryfile)
local result = try { function () return os.iorunv(patchelf.program, {"--print-rpath", binaryfile}) end }
if result then
result = result:trim()
if #result > 0 then
list = path.splitenv(result)
end
end
end
return list
end


-- $ otool -l build/iphoneos/arm64/release/test
-- build/iphoneos/arm64/release/test:
-- cmd LC_RPATH
Expand Down Expand Up @@ -163,6 +188,20 @@ function _get_rpath_list_by_otool(binaryfile, opt)
return list
end

-- patchelf --add-rpath binaryfile
function _insert_rpath_by_patchelf(binaryfile, rpath, opt)
local plat = opt.plat or os.host()
local arch = opt.arch or os.arch()
if plat ~= "linux" and plat ~= "bsd" and plat ~= "android" and plat ~= "cross" then
return false
end
local ok = try { function ()
os.runv("patchelf", {"--add-rpath", rpath, binaryfile})
return true
end }
return ok
end

-- install_name_tool -add_rpath <rpath> binaryfile
function _insert_rpath_by_install_name_tool(binaryfile, rpath, opt)
local plat = opt.plat or os.host()
Expand Down Expand Up @@ -191,6 +230,38 @@ function _change_rpath_by_install_name_tool(binaryfile, rpath_old, rpath_new, op
return ok
end

-- use patchelf to remove the given rpath
function _remove_rpath_by_patchelf(binaryfile, rpath, opt)
local plat = opt.plat or os.host()
local arch = opt.arch or os.arch()
if plat ~= "linux" and plat ~= "bsd" and plat ~= "android" and plat ~= "cross" then
return false
end
local ok = try { function ()
local result = os.iorunv("patchelf", {"--print-rpath", binaryfile})
if result then
local rpaths_new = {}
local removed = false
for _, p in ipairs(path.splitenv(result:trim())) do
if p ~= rpath then
table.insert(rpaths_new, p)
else
removed = true
end
end
if removed then
if #rpaths_new > 0 then
os.runv("patchelf", {"--set-rpath", path.joinenv(rpaths_new), binaryfile})
else
os.runv("patchelf", {"--remove-rpath", binaryfile})
end
end
end
return true
end }
return ok
end

-- install_name_tool -delete_rpath <rpath> binaryfile
function _remove_rpath_by_install_name_tool(binaryfile, rpath, opt)
local plat = opt.plat or os.host()
Expand All @@ -205,12 +276,27 @@ function _remove_rpath_by_install_name_tool(binaryfile, rpath, opt)
return ok
end

-- patchelf --remove-rpath binaryfile
function _clean_rpath_by_patchelf(binaryfile, opt)
local plat = opt.plat or os.host()
local arch = opt.arch or os.arch()
if plat ~= "linux" and plat ~= "bsd" and plat ~= "android" and plat ~= "cross" then
return false
end
local ok = try { function ()
os.runv("patchelf", {"--remove-rpath", binaryfile})
return true
end }
return ok
end

-- get rpath list
function list(binaryfile, opt)
opt = opt or {}
local ops = {
_get_rpath_list_by_objdump,
_get_rpath_list_by_readelf
_get_rpath_list_by_readelf,
_get_rpath_list_by_patchelf
}
if is_host("macosx") then
table.insert(ops, 1, _get_rpath_list_by_otool)
Expand All @@ -226,52 +312,93 @@ end
-- insert rpath
function insert(binaryfile, rpath, opt)
opt = opt or {}
local ops = {}
local ops = {
_insert_rpath_by_patchelf
}
if is_host("macosx") then
table.insert(ops, 1, _insert_rpath_by_install_name_tool)
end
rpath = _replace_rpath_vars(rpath, opt)
local done = false
for _, op in ipairs(ops) do
if op(binaryfile, rpath, opt) then
done = true
break
end
end
if not done then
wprint("cannot insert rpath to %s, no rpath utility available, maybe we need to install patchelf", binaryfile)
end
end

-- change rpath
function change(binaryfile, rpath_old, rpath_new, opt)
-- remove rpath
function remove(binaryfile, rpath, opt)
opt = opt or {}
local ops = {}
local ops = {
_remove_rpath_by_patchelf
}
if is_host("macosx") then
table.insert(ops, 1, _change_rpath_by_install_name_tool)
table.insert(ops, 1, _remove_rpath_by_install_name_tool)
end
rpath_old = _replace_rpath_vars(rpath_old, opt)
rpath_new = _replace_rpath_vars(rpath_new, opt)
rpath = _replace_rpath_vars(rpath, opt)
for _, op in ipairs(ops) do
if op(binaryfile, rpath_old, rpath_new, opt) then
if op(binaryfile, rpath, opt) then
break
end
end
end

-- remove rpath
function remove(binaryfile, rpath, opt)
-- change rpath
function change(binaryfile, rpath_old, rpath_new, opt)
local function _change_rpath_by_generic(binaryfile, rpath_old, rpath_new, opt)
remove(binaryfile, rpath_old, opt)
insert(binaryfile, rpath_new, opt)
return true
end

opt = opt or {}
local ops = {}
local ops = {
_change_rpath_by_generic
}
if is_host("macosx") then
table.insert(ops, 1, _remove_rpath_by_install_name_tool)
table.insert(ops, 1, _change_rpath_by_install_name_tool)
end
rpath = _replace_rpath_vars(rpath, opt)
rpath_old = _replace_rpath_vars(rpath_old, opt)
rpath_new = _replace_rpath_vars(rpath_new, opt)
local done = false
for _, op in ipairs(ops) do
if op(binaryfile, rpath, opt) then
if op(binaryfile, rpath_old, rpath_new, opt) then
done = true
break
end
end
if not done then
wprint("cannot change rpath to %s, no rpath utility available, maybe we need to install patchelf", binaryfile)
end
end

-- clean rpath
function clean(binaryfile, opt)
for _, rpath in ipairs(list(binaryfile, opt)) do
remove(binaryfile, rpath, opt)
local function _clean_rpath_by_generic(binaryfile, opt)
for _, rpath in ipairs(list(binaryfile, opt)) do
remove(binaryfile, rpath, opt)
end
return true
end

opt = opt or {}
local ops = {
_clean_rpath_by_patchelf,
_clean_rpath_by_generic
}
local done = false
for _, op in ipairs(ops) do
if op(binaryfile, opt) then
done = true
break
end
end
if not done then
wprint("cannot clean rpath %s, no rpath utility available, maybe we need to install patchelf", binaryfile)
end
end
Loading