Skip to content

Commit 465bd3c

Browse files
committed
Use separated functions for exporting symbols
1 parent d533a05 commit 465bd3c

File tree

4 files changed

+92
-73
lines changed

4 files changed

+92
-73
lines changed

src/SPC/builder/linux/LinuxBuilder.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,8 @@ protected function buildEmbed(): void
318318
if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'static') {
319319
$AR = getenv('AR') ?: 'ar';
320320
f_passthru("{$AR} -t " . BUILD_LIB_PATH . "/libphp.a | grep '\\.a$' | xargs -n1 {$AR} d " . BUILD_LIB_PATH . '/libphp.a');
321+
// export dynamic symbols
322+
SystemUtil::exportDynamicSymbols(BUILD_LIB_PATH . '/libphp.a');
321323
}
322324

323325
if (!$this->getOption('no-strip', false) && file_exists(BUILD_LIB_PATH . '/' . $realLibName)) {

src/SPC/builder/macos/MacOSBuilder.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,8 @@ protected function buildEmbed(): void
252252
if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'static') {
253253
$AR = getenv('AR') ?: 'ar';
254254
f_passthru("{$AR} -t " . BUILD_LIB_PATH . "/libphp.a | grep '\\.a$' | xargs -n1 {$AR} d " . BUILD_LIB_PATH . '/libphp.a');
255+
// export dynamic symbols
256+
SystemUtil::exportDynamicSymbols(BUILD_LIB_PATH . '/libphp.a');
255257
}
256258
$this->patchPhpScripts();
257259
}

src/SPC/builder/traits/UnixSystemUtilTrait.php

Lines changed: 81 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,88 @@
44

55
namespace SPC\builder\traits;
66

7-
/**
8-
* Unix 系统的工具函数 Trait,适用于 Linux、macOS
9-
*/
7+
use SPC\exception\ExecutionException;
8+
use SPC\exception\WrongUsageException;
9+
use SPC\toolchain\ToolchainManager;
10+
use SPC\toolchain\ZigToolchain;
11+
use SPC\util\SPCTarget;
12+
1013
trait UnixSystemUtilTrait
1114
{
1215
/**
13-
* @param string $name 命令名称
14-
* @param array $paths 寻找的目标路径(如果不传入,则使用环境变量 PATH)
15-
* @return null|string 找到了返回命令路径,找不到返回 null
16+
* Export static library dynamic symbols to a .dynsym file.
17+
* It will export to "/path/to/libxxx.a.dynsym".
18+
*
19+
* @param string $lib_file Static library file path (e.g. /path/to/libxxx.a)
20+
*/
21+
public static function exportDynamicSymbols(string $lib_file): void
22+
{
23+
// check
24+
if (!is_file($lib_file)) {
25+
throw new WrongUsageException("The lib archive file {$lib_file} does not exist, please build it first.");
26+
}
27+
// shell out
28+
$cmd = 'nm -g --defined-only -P ' . escapeshellarg($lib_file);
29+
$result = shell()->execWithResult($cmd);
30+
if ($result[0] !== 0) {
31+
throw new ExecutionException($cmd, 'Failed to get defined symbols from ' . $lib_file);
32+
}
33+
// parse shell output and filter
34+
$defined = [];
35+
foreach ($result[1] as $line) {
36+
$line = trim($line);
37+
if ($line === '' || str_ends_with($line, '.o:') || str_ends_with($line, '.o]:')) {
38+
continue;
39+
}
40+
$name = strtok($line, " \t");
41+
if (!$name) {
42+
continue;
43+
}
44+
$name = preg_replace('/@.*$/', '', $name);
45+
if ($name !== '' && $name !== false) {
46+
$defined[] = $name;
47+
}
48+
}
49+
$defined = array_unique($defined);
50+
sort($defined);
51+
// export
52+
if (SPCTarget::getTargetOS() === 'Linux') {
53+
file_put_contents("{$lib_file}.dynsym", "{\n" . implode("\n", array_map(fn ($x) => " {$x};", $defined)) . "};\n");
54+
} else {
55+
file_put_contents("{$lib_file}.dynsym", implode("\n", $defined) . "\n");
56+
}
57+
}
58+
59+
/**
60+
* Get linker flag to export dynamic symbols from a static library.
61+
*
62+
* @param string $lib_file Static library file path (e.g. /path/to/libxxx.a)
63+
* @return null|string Linker flag to export dynamic symbols, null if no .dynsym file found
64+
*/
65+
public static function getDynamicExportedSymbols(string $lib_file): ?string
66+
{
67+
$symbol_file = "{$lib_file}.dynsym";
68+
if (!is_file($symbol_file)) {
69+
return null;
70+
}
71+
// https://github.com/ziglang/zig/issues/24662
72+
if (ToolchainManager::getToolchainClass() === ZigToolchain::class) {
73+
return '-Wl,--export-dynamic';
74+
}
75+
// macOS
76+
if (SPCTarget::getTargetOS() !== 'Linux') {
77+
return "-Wl,-exported_symbols_list,{$symbol_file}";
78+
}
79+
return "-Wl,--dynamic-list={$symbol_file}";
80+
}
81+
82+
/**
83+
* Find a command in given paths or system PATH.
84+
* If $name is an absolute path, check if it exists.
85+
*
86+
* @param string $name Command name or absolute path
87+
* @param array $paths Paths to search, if empty, use system PATH
88+
* @return null|string Absolute path of the command if found, null otherwise
1689
*/
1790
public static function findCommand(string $name, array $paths = []): ?string
1891
{
@@ -31,6 +104,8 @@ public static function findCommand(string $name, array $paths = []): ?string
31104
}
32105

33106
/**
107+
* Make environment variable string for shell command.
108+
*
34109
* @param array $vars Variables, like: ["CFLAGS" => "-Ixxx"]
35110
* @return string like: CFLAGS="-Ixxx"
36111
*/

src/SPC/builder/unix/UnixBuilderBase.php

Lines changed: 7 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace SPC\builder\unix;
66

77
use SPC\builder\BuilderBase;
8+
use SPC\builder\linux\SystemUtil as LinuxSystemUtil;
89
use SPC\exception\SPCInternalException;
910
use SPC\exception\ValidationException;
1011
use SPC\exception\WrongUsageException;
@@ -15,7 +16,6 @@
1516
use SPC\store\pkg\GoXcaddy;
1617
use SPC\toolchain\GccNativeToolchain;
1718
use SPC\toolchain\ToolchainManager;
18-
use SPC\toolchain\ZigToolchain;
1919
use SPC\util\DependencyUtil;
2020
use SPC\util\GlobalEnvManager;
2121
use SPC\util\SPCConfigUtil;
@@ -32,68 +32,6 @@ abstract class UnixBuilderBase extends BuilderBase
3232
/** @var string LD flags */
3333
public string $arch_ld_flags;
3434

35-
private ?string $dynamic_export_list = null;
36-
37-
public function getDynamicExportSymbolsArgument(): ?string
38-
{
39-
if ($this->dynamic_export_list) {
40-
return $this->dynamic_export_list;
41-
}
42-
if (SPCTarget::isStatic()) {
43-
return null;
44-
}
45-
46-
$defined = [];
47-
$libphp = BUILD_LIB_PATH . '/libphp.a';
48-
if (!is_file($libphp)) {
49-
throw new WrongUsageException('You must build libphp.a before calling this function.');
50-
}
51-
52-
if ($out = shell_exec('nm -g --defined-only -P ' . escapeshellarg($libphp) . ' 2>/dev/null')) {
53-
foreach (preg_split('/\R/', trim($out)) as $line) {
54-
if ($line === '' || str_ends_with($line, '.o:') || str_ends_with($line, '.o]:')) {
55-
continue;
56-
}
57-
$name = strtok($line, " \t");
58-
if (!$name) {
59-
continue;
60-
}
61-
$name = preg_replace('/@.*$/', '', $name);
62-
if ($name !== '' && $name !== false) {
63-
$defined[] = $name;
64-
}
65-
}
66-
}
67-
$defined = array_unique($defined);
68-
sort($defined);
69-
70-
$exportList = BUILD_LIB_PATH . '/export-dynamic.list';
71-
$lines = [];
72-
if (SPCTarget::getTargetOS() === 'Linux') {
73-
$lines[] = '{';
74-
foreach ($defined as $sym) {
75-
$lines[] = " {$sym};";
76-
}
77-
$lines[] = '};';
78-
} else {
79-
foreach ($defined as $sym) {
80-
$lines[] = $sym;
81-
}
82-
}
83-
file_put_contents($exportList, implode("\n", $lines) . "\n");
84-
85-
$argument = "-Wl,--dynamic-list={$exportList}";
86-
if (ToolchainManager::getToolchainClass() === ZigToolchain::class) {
87-
$argument = '-Wl,--export-dynamic'; // https://github.com/ziglang/zig/issues/24662
88-
}
89-
if (SPCTarget::getTargetOS() !== 'Linux') {
90-
$argument = "-Wl,-exported_symbols_list,{$exportList}";
91-
}
92-
93-
$this->dynamic_export_list = $argument;
94-
return $argument;
95-
}
96-
9735
public function proveLibs(array $sorted_libraries): void
9836
{
9937
// search all supported libs
@@ -215,11 +153,13 @@ protected function sanityCheck(int $build_target): void
215153
foreach (glob(BUILD_LIB_PATH . "/libphp*.{$suffix}") as $file) {
216154
unlink($file);
217155
}
218-
if ($dynamicSymbolsArgument = $this->getDynamicExportSymbolsArgument()) {
219-
$dynamic_exports = ' ' . $dynamicSymbolsArgument;
156+
// calling linux system util in other unix OS is okay
157+
if ($dynamic_exports = LinuxSystemUtil::getDynamicExportedSymbols(BUILD_LIB_PATH . '/libphp.a')) {
158+
$dynamic_exports = ' ' . $dynamic_exports;
220159
}
221160
}
222-
[$ret, $out] = shell()->cd($sample_file_path)->execWithResult(getenv('CC') . ' -o embed embed.c ' . $lens . ' ' . $dynamic_exports);
161+
$cc = getenv('CC');
162+
[$ret, $out] = shell()->cd($sample_file_path)->execWithResult("{$cc} -o embed embed.c {$lens} {$dynamic_exports}");
223163
if ($ret !== 0) {
224164
throw new ValidationException(
225165
'embed failed sanity check: build failed. Error message: ' . implode("\n", $out),
@@ -338,7 +278,7 @@ protected function buildFrankenphp(): void
338278
if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'shared') {
339279
$libphpVersion = preg_replace('/\.\d+$/', '', $libphpVersion);
340280
} else {
341-
if ($dynamicSymbolsArgument = $this->getDynamicExportSymbolsArgument()) {
281+
if ($dynamicSymbolsArgument = LinuxSystemUtil::getDynamicExportedSymbols(BUILD_LIB_PATH . '/libphp.a')) {
342282
$dynamic_exports = ' ' . $dynamicSymbolsArgument;
343283
}
344284
}

0 commit comments

Comments
 (0)