- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
请参阅文章末尾的编辑以回应 Employed Russian的评论
在继续之前,我知道将函数命名为 error
通常是不好的做法,因为它可能会与 libc 中的类似函数发生冲突,但这是我在使用某些第三方软件时遇到的问题我几乎无法控制。另外,我真的很想知道这个错误是从哪里来的:-)
我遇到的问题是,当通过 Python 解释器执行而不是调用我的 error
函数的本地实现时,下面的代码实际上是在调用 libC 的 error
代替函数(如下面的 GDB 堆栈跟踪所示)。
在另一个 C 程序中简单地编译相同的代码时,我没有遇到这样的问题。有人知道那是从哪里来的吗?它与 Python 加载共享库的方式有关吗?
#include <stdio.h>
#include <Python.h>
static PyObject* call_error(PyObject *self, PyObject *args);
static PyMethodDef module_methods[] = {
{"error", call_error, METH_NOARGS, "call error"},
{NULL, NULL, 0, NULL}
};
static struct PyModuleDef module_defs = {
PyModuleDef_HEAD_INIT,
"Test", "Test", -1, module_methods, NULL, NULL, NULL, NULL};
PyObject* PyInit_Test(void)
{
PyObject *module = PyModule_Create(&module_defs);
return module;
}
void error(const char* fmt, ...);
PyObject* call_error(PyObject *self, PyObject *args)
{
error("Error!");
Py_RETURN_NONE;
}
void error(const char* fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
}
这是使用 python3 -c "import Test; Test.error()"
GNU gdb (Ubuntu 8.1-0ubuntu3) 8.1.0.20180409-git
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from python3...(no debugging symbols found)...done.
(gdb) r -c 'import Test; Test.error()'
Starting program: /usr/bin/python3 -c 'import Test; Test.error()'
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
/usr/bin/python3:
Program received signal SIGSEGV, Segmentation fault.
__strchrnul_sse2 () at ../sysdeps/x86_64/multiarch/../strchr.S:32
32 ../sysdeps/x86_64/multiarch/../strchr.S: No such file or directory.
(gdb) where
#0 __strchrnul_sse2 () at ../sysdeps/x86_64/multiarch/../strchr.S:32
#1 0x00007ffff6c2c432 in __find_specmb (format=0x4 <error: Cannot access memory at
# address 0x4>) at printf-parse.h:108
#2 _IO_vfprintf_internal (s=0x7fffffffae60, format=0x4 <error: Cannot access memory at
# address 0x4>, ap=0x7fffffffd5b0) at vfprintf.c:1320
#3 0x00007ffff6c2f680 in buffered_vfprintf (s=s@entry=0x7ffff6fbd680 <_IO_2_1_stderr_>,
# format=format@entry=0x4 <error: Cannot access memory at address 0x4>,
# args=args@entry=0x7fffffffd5b0) at vfprintf.c:2329
#4 0x00007ffff6c2c726 in _IO_vfprintf_internal (s=0x7ffff6fbd680 <_IO_2_1_stderr_>,
# format=format@entry=0x4 <error: Cannot access memory at address 0x4>,
# ap=ap@entry=0x7fffffffd5b0) at vfprintf.c:1301
#5 0x00007ffff6cef9bb in error_tail (status=status@entry=-161613509,
# errnum=errnum@entry=0, message=message@entry=0x4 <error: Cannot access memory at
# address 0x4>, args=args@entry=0x7fffffffd5b0) at error.c:271
#6 0x00007ffff6cefb3d in __error (status=-161613509, errnum=0, message=0x4
# <error: Cannot access memory at address 0x4>) at error.c:321
#7 0x00007ffff65df82e in call_error (self=0x7ffff67f3548, args=0x0) at test.c:24
#8 0x00000000004c5352 in _PyCFunction_FastCallKeywords ()
#9 0x000000000054ffe4 in ?? ()
#10 0x00000000005546cf in _PyEval_EvalFrameDefault ()
#11 0x000000000054fbe1 in ?? ()
#12 0x0000000000550b93 in PyEval_EvalCode ()
#13 0x000000000042c4ca in PyRun_SimpleStringFlags ()
#14 0x0000000000441918 in Py_Main ()
#15 0x0000000000421ff4 in main ()
我确实考虑过导入 Python 模块的 dlopen
问题,实际上下面的代码编译和运行得很好并打印出来:
> ./main
Hi there
main.c
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <errno.h>
#include <stdarg.h>
typedef void*(*arbitrary)();
extern void error(const char* fmt, ...);
int main(int argc, char **argv)
{
void *handle;
arbitrary my_function;
handle = dlopen("./libtest.so", RTLD_LAZY | RTLD_GLOBAL);
if (!handle) {
fprintf(stderr, "%s\n", dlerror());
exit(EXIT_FAILURE);
}
dlerror(); /* Clear any existing error */
*(void**)(&my_function) = dlsym(handle,"foo");
(void) my_function();
// Note: binding using dlsym(handle, "error") works too
dlclose(handle);
exit(EXIT_SUCCESS);
}
test.c
#include <stdio.h>
#include <stdarg.h>
extern void error(const char* fmt, ...);
extern void foo(void);
void foo(void)
{
error("Hi there\n");
}
void error(const char* fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
}
最佳答案
this is an issue I have with some third-party software on which I have little control.
如果您有此第三方软件的源代码,您可以对其进行编辑,或使用宏技巧重命名函数,例如-Derror=foo_error
.
如果您只有一个存档库,请使用 objcopy --redefine-symbol ...
。
如果您只有一个共享库,我不知道可行的解决方案。
Does it have to do with the way Python loads shared libraries ?
有点。发生的事情是动态加载器将对 error
的引用解析为该函数的最早导出定义。
当您将 error
链接到主 a.out
时,该定义是链接器搜索顺序中的第一个,因此它“获胜”。
当您使用 dlopen
加载包含 error
的 libfoo.so
时(这是 Python 为 import
),该库在 libc.so.6
之后加载,这意味着 libc.so.6
在加载程序搜索顺序中出现较早,及其定义“获胜”。
您不需要 Python 也能看到这一点:编写一个使用 dlopen
的简单测试,同样的问题会出现在其中。
更新:
I had written a small test case
您的测试用例确实证实了我的回答。您可能没有正确构建它。
$ gcc -fPIC -shared -o libtest.so test.c
$ gcc main.c -ldl
这里调用了“错误的”error
,因为库加载的顺序是:a.out
,libc.so.6
, 然后 libtest.so
:
$ ./a.out
./a.out: UH��H�=�: Unknown error 640192728
但你可能做的是这样的:
$ gcc main.c ./libtest.so -ldl
这里加载库的顺序是a.out
,libtest.so
(因为a.out
直接 取决于 libtest.so
),then libc.so.6
,“正确的”error
得到称为:
$ ./a.out
Hi there
关于对 error() 的 Python C API 调用绑定(bind)到 libc 实现而不是本地实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51619095/
我有一个 foo 类,它有一个 bar 方法,它接受可调用的东西(函数指针/仿函数)。这个可调用的东西应该作为绑定(bind)元素传递给另一个方法 doit 和第三个方法 bar_cb 方法。 #in
我正在尝试在我的 WPF 4.0 应用程序(使用 VS 2010 Pro RTM)中创建自定义 TabItem 模板/样式,但尽管一切似乎都正常工作,但我注意到跟踪窗口中存在绑定(bind)错误。 我
作为一名刚接触 Android 的开发人员,我想我可能误解了绑定(bind)服务。 我创建了一项服务来结束对服务器的访问。作为此服务的一部分,该服务正在监听多播地址,以识别本地网络上的设备何时出现和消
这个问题在这里已经有了答案: What is the use of the JavaScript 'bind' method? (23 个回答) 关闭 7 年前。 所以我一直在尝试了解一些 JS 上
我不明白这三种语法之间的区别: where a = f (b) do a <- f (b) do let a = f (b) 我确实明白了a <- f(b)与其他两个不同,在大多数情况下,我尝试了所有
我在将 Cocoa 项目从手动同步接口(interface)模型转换为绑定(bind)模型时遇到问题,这样我就不必担心接口(interface)粘合代码。 我关注了 CocoaDevCentral C
我正在尝试找出一种好的方法来对处理大数据集的代码进行并行化,然后将结果数据导入 RavenDb。 数据处理受 CPU 限制和数据库导入 IO 限制。 我正在寻找一种解决方案,以对 Environmen
我正在 foreach 循环中生成单选按钮。我试图将选中的属性绑定(bind)到父级中的基本可观察值。不幸的是,当单击单选按钮时,父级的属性似乎没有在单击处理程序中更新。 基于一些previous w
在我的 Windows Phone 应用程序中,我有两个 LongListSelectors并排在页面上。我想做到这一点,以便当用户滚动其中一个时,另一个滚动相同的量。 两个 LongListSele
我在网上看到这个问题准备面试: Given a non-preemptive kernel which type of process will get affected morein terms o
我有一个 foreach 绑定(bind),如下所示: Summary Permitting 原因是有两个选项卡始终存在,并且我根据是否添加了其他选项卡来添加其他选项
任何人都有绑定(bind)相同的情况DataContext到 TextBlock 中的 Text 属性(例如)。 我必须分配 DataContext以我的风格反射(reflect)基于 Datacon
给定以下代码: Login 和下面的javascript $(function () { $('#btnLogin').click(function () { co
我使用 boost::asio 创建了一个服务器。我在绑定(bind)到端点时遇到问题。所以,如果我在构造函数中初始化一个接受器: Server::Server(QWidget *parent) :
我正在将现有项目从 MySQL 转换为 Postgres。代码中有相当多的原始 SQL 文字使用 ? 作为占位符,例如 SELECT id FROM users WHERE
似乎在绑定(bind)某些数据时出错了,有人可以帮我解决我哪里出错了,尽管我无法弄清楚。 真的不需要在这里显示太多,这是 Binding,我已经通过移除背景并在其中放置颜色来测试背景,效果很好。 编辑
我正在尝试使用 wcf 构建一个 http 监听器(web 服务)。这个监听器是一个更大的桌面应用程序的一部分。此桌面应用程序还会调用 http 监听器。 当监听器接收到数据时,它应该被传递到桌面应用
嘿嘿。 我正在使用 Node.JS 和 child_process 来生成 bash 进程。我试图了解我是否正在执行 I/O 绑定(bind)、CPU 绑定(bind)或两者兼而有之。 我正在使用 p
尝试执行以下操作并出现“Got interpolation ({{}}) where expression was expected”错误。 {{item.name}} 谢谢!
我有一个导入的 Java 库,它是我解决方案中的“绑定(bind)库”项目。 我正在尝试从解决方案中的另一个项目绑定(bind)到第 3 方库中的服务。 第 3 方库文档 [在 java 中] 非常简
我是一名优秀的程序员,十分优秀!