Skip to content
Open
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
64 changes: 63 additions & 1 deletion mesonbuild/backend/ninjabackend.py
Original file line number Diff line number Diff line change
Expand Up @@ -1102,7 +1102,49 @@ def generate_target(self, target) -> None:
final_obj_list = self.generate_prelink(target, obj_list)
else:
final_obj_list = obj_list
elem = self.generate_link(target, outname, final_obj_list, linker, pch_objects, stdlib_args=stdlib_args)

# In AIX, projects like postgres where the shared modules/plugins require symbols from the main binary.
# The postgres binary in case of postgressql needs to have every shared library that it links to export
# all symbols except those undefined, in its symbol table thereby telling the linker
# the symbols it exports via the -Wl,-bE:<path_to_export_file> option, where export file is a file having
# all the symbols the shared library will export. For example, the initdb binary of postgres in AIX. If we
# check the dependency of the same, ldd initdb, we get /lib/libc.a(shr_64.o),
# /lib/libpthreads.a(shr_xpg5_64.o), /libpq/libpq.a(libpq.so.5.18) and so on. We need to tell the linker
# via the export file to add the symbols exported by libpq.a to libpq.a's symbol table, so at runtime linking
# the linker knows where these symbols came from and can resolve the same. The below code does two things.
# One with ld -r -o <name>_SUBSYS.o <object files> we create a single object file that will have all the
# objects needed to create a target shared library.
# Two: Using this object file and nm command extract all the symbols using 'nm' command and create the
# export list. Then use this export list in the shared library command via -Wl,-bE:<path_to_export_file>
# option which tells the linker that this shared library exports these symbols in export file.
# Example:
# build /getpwuid-preload.so_SUBSYS.o: Export_List_AIX_Object /getpwuid-preload.so.p/getpwuid-preload.c.o
# IMPLIB = /getpwuid-preload.a
# build /getpwuid-preload.so.exp: AIX_SHSYM /getpwuid-preload.so_SUBSYS.o
# IMPLIB = /getpwuid-preload.a
# build /getpwuid-preload.so: c_LINKER /getpwuid-preload.so.p/getpwuid-preload.c.o /getpwuid-preload.so.exp
# LINK_ARGS = -Wl,-bE:/getpwuid-preload.so.exp

symname = ""
if (isinstance(target, build.SharedLibrary) and not isinstance(target, build.SharedModule)
and self.environment.machines[target.for_machine].is_aix()):
target_file = os.path.join(self.get_target_dir(target), target.get_filename() + '_SUBSYS.o')
symname = os.path.join(self.get_target_dir(target), target.get_filename() + '_SUBSYS.o')
Export_List_AIX_Object_elem = NinjaBuildElement(self.all_outputs, symname, 'Export_List_AIX_Object', final_obj_list)
Export_List_AIX_Object_elem.add_item('IMPLIB', self.get_target_filename_for_linking(target))
self.add_build(Export_List_AIX_Object_elem)

symname = target.get_filename() + '.exp'
AIX_SHSYM_elem = NinjaBuildElement(self.all_outputs, os.path.join(self.get_target_dir(target), symname), 'AIX_SHSYM', target_file)
AIX_SHSYM_elem.add_item('IMPLIB', self.get_target_filename_for_linking(target))
self.add_build(AIX_SHSYM_elem)

elem = self.generate_link(target, outname, final_obj_list + [os.path.join(self.get_target_dir(target), symname)], linker, pch_objects, stdlib_args=stdlib_args)

# All other targets follow the normal method.
else:
elem = self.generate_link(target, outname, final_obj_list, linker, pch_objects, stdlib_args=stdlib_args)

self.generate_dependency_scan_target(target, compiled_sources, source2object, fortran_order_deps)
self.add_build(elem)
#In AIX, we archive shared libraries. If the instance is a shared library, we add a command to archive the shared library
Expand Down Expand Up @@ -2439,6 +2481,22 @@ def generate_dynamic_link_rules(self) -> None:
options = {}
self.add_rule(NinjaRule(rule, cmdlist, args, description, **options, extra=None))

rule = 'Export_List_AIX_Object{}'.format(self.get_rule_suffix(for_machine))
description = 'Export file object creation for AIX'
cmdlist = ['/usr/bin/ld', '-r', '-o', '$out', '$in']
args = []
options = {}
self.add_rule(NinjaRule(rule, cmdlist, args, description, **options, extra=None))

args = self.environment.get_build_command() + \
['--internal', 'symbolextractor', self.environment.get_build_dir(), '$in', '$IMPLIB', '$out']
if self.environment.machines[for_machine].is_aix():
symrule = 'AIX_SHSYM'
symcmd = args + ['$CROSS']
syndesc = 'Generating AIX symbol file $out'
synstat = 'restat = 1'
self.add_rule(NinjaRule(symrule, symcmd, [], syndesc, extra=synstat))

args = self.environment.get_build_command() + \
['--internal',
'symbolextractor',
Expand Down Expand Up @@ -3417,6 +3475,10 @@ def get_target_type_link_args(self, target, linker):
# This is only visited when building for Windows using either GCC or Visual Studio
if target.import_filename:
commands += linker.gen_import_library_args(self.get_import_filename(target))
# Add export file option in command for AIX.
if self.environment.machines[target.for_machine].is_aix():
if not isinstance(target, build.SharedModule):
commands += ['-Wl,-bE:' + self.get_target_filename(target) + '.exp']
elif isinstance(target, build.StaticLibrary):
produce_thin_archive = self.allow_thin_archives[target.for_machine] and not target.should_install()
commands += linker.get_std_link_args(self.environment, produce_thin_archive)
Expand Down
18 changes: 18 additions & 0 deletions mesonbuild/scripts/symbolextractor.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from .. import mlog
from ..mesonlib import Popen_safe
import argparse
import subprocess

parser = argparse.ArgumentParser()

Expand Down Expand Up @@ -122,6 +123,21 @@ def solaris_syms(libfilename: str, outfilename: str) -> None:
finally:
os.environ['PATH'] = origpath

def aix_syms(libfilename: str, outfilename: str) -> None:
nm_output = subprocess.run(rf"nm -BCg {libfilename} | egrep ' [TDB] ' | sed -e 's/.* //' | egrep -v '\\$' | sed -e 's/^[.]//' | sort | uniq",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shell pipelines are never acceptable. This same functionality must be provided with Python.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. Thanks, @jpakkane. I will definitely try to change this and will keep it in my mind. As of now, we are redesigning this pull request with @eli-schwartz. Post that I will try.

In case of any constraints will get back.

shell=True,
check=True,
text=True,
capture_output=True)
# First line in export file will be #!
try:
with open(outfilename, 'w', encoding="utf-8") as f:
f.write('#!\n')
f.write(nm_output.stdout)
print("Symbols are written to ", outfilename)
except Exception as e:
print(f"Exception: Could not generate export file {e}")

def osx_syms(libfilename: str, outfilename: str) -> None:
# Get the name of the library
output = call_tool('otool', ['-l', libfilename])
Expand Down Expand Up @@ -299,6 +315,8 @@ def gen_symbols(libfilename: str, impfilename: str, outfilename: str, cross_host
dummy_syms(outfilename)
elif mesonlib.is_sunos():
solaris_syms(libfilename, outfilename)
elif mesonlib.is_aix():
aix_syms(libfilename, outfilename)
else:
if not os.path.exists(TOOL_WARNING_FILE):
mlog.warning('Symbol extracting has not been implemented for this '
Expand Down
Loading