Oracle 19c非安装账号下SQLAllocEnv失败及段错误排查
Oracle ODBC动态加载库跨账号运行问题排查
问题背景
我编写了一段动态加载Oracle ODBC库libsqora.so.19.1并分配SQLHENV的示例程序,编译命令如下:
gcc -o test test.c -ldl -lodbc
程序代码:
#include <dlfcn.h> #include <stdio.h> #include <stdlib.h> #include <sql.h> #include <sqlext.h> int main() { void* oracleLib = dlopen("libsqora.so.19.1", RTLD_NOW); if (!oracleLib) { fprintf(stderr, "failed open Oracle ODBC library: %s\n", dlerror()); return 1; } SQLHENV hEnv; SQLRETURN (*SQLAllocHandle)(SQLSMALLINT HandleType, SQLHANDLE InputHandle, SQLHANDLE *OutputHandle); SQLRETURN (*SQLSetEnvAttr)(SQLHENV EnvironmentHandle, SQLINTEGER Attribute, SQLPOINTER Value, SQLINTEGER StringLength); //SQLRETURN (*SQLAllocConnect)(SQLHENV EnvironmentHandle, SQLHDBC *ConnectionHandle); SQLRETURN (*SQLDriverConnectW)(SQLHDBC ConnectionHandle, SQLHWND WindowHandle, SQLCHAR *InConnectionString, SQLSMALLINT StringLength1, SQLCHAR *OutConnectionString, SQLSMALLINT BufferLength, SQLSMALLINT *StringLength2Ptr, SQLUSMALLINT DriverCompletion); SQLRETURN (*SQLFreeHandle)(SQLSMALLINT HandleType, SQLHANDLE Handle); SQLAllocHandle = dlsym(oracleLib, "SQLAllocHandle"); SQLSetEnvAttr = dlsym(oracleLib, "SQLSetEnvAttr"); //SQLAllocConnect = dlsym(oracleLib, "SQLAllocConnect"); SQLDriverConnectW = dlsym(oracleLib, "SQLDriverConnectW"); SQLFreeHandle = dlsym(oracleLib, "SQLFreeHandle"); if (!SQLAllocHandle || !SQLSetEnvAttr || !SQLAllocConnect || !SQLDriverConnectW || !SQLFreeHandle) { fprintf(stderr, "can not find ODBC API: %s\n", dlerror()); dlclose(oracleLib); return 1; } SQLRETURN sReturn; sReturn = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv); if(sReturn < 0 ) { fprintf(stderr, "%d SQLAllocHandle failed\n", __LINE__ ); dlclose(oracleLib); return -1; } sReturn = SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0); if(sReturn < 0 ) { fprintf(stderr, "%d SQLSetEnvAttr failed\n", __LINE__ ); dlclose(oracleLib); return -1; } SQLHDBC hDbc; sReturn = SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc); sReturn = SQLFreeHandle(SQL_HANDLE_DBC, hDbc); sReturn = SQLFreeHandle(SQL_HANDLE_ENV, hEnv); dlclose(oracleLib); return 0; }
该程序在Oracle安装账号下运行正常,但切换到其他账号执行时,出现SQLHENV分配失败,且在dlclose过程中发生段错误。已检查libsqora.so.19.1的权限,其ldd输出如下:
ldd libsqora.so.19.1 linux-vdso.so.1 => (0x00007fff713f2000) libdl.so.2 => /lib64/libdl.so.2 (0x00007fce7feca000) libm.so.6 => /lib64/libm.so.6 (0x00007fce7fbc8000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fce7f9ac000) libnsl.so.1 => /lib64/libnsl.so.1 (0x00007fce7f792000) librt.so.1 => /lib64/librt.so.1 (0x00007fce7f58a000) libaio.so.1 => /lib64/libaio.so.1 (0x00007fce7f388000) libresolv.so.2 => /lib64/libresolv.so.2 (0x00007fce7f16e000) libclntsh.so.19.1 => /home/lkh/workspace/libclntsh.so.19.1 (0x00007fce7b160000) libclntshcore.so.19.1 => /home/lkh/workspace/libclntshcore.so.19.1 (0x00007fce7abc0000) libodbcinst.so.2 => /lib64/libodbcinst.so.2 (0x00007fce7a9ae000) libc.so.6 => /lib64/libc.so.6 (0x00007fce7a5e0000) /lib64/ld-linux-x86-64.so.2 (0x00007fce80388000) libnnz19.so => /home/oracle/oracle/lib/libnnz19.so (0x00007fce79e98000) libltdl.so.7 => /lib64/libltdl.so.7 (0x00007fce79c8e000)
请问我遗漏了哪些配置或检查项?
排查方向及遗漏项
1. Oracle依赖库的目录权限
从ldd输出可见,libnnz19.so位于/home/oracle/oracle/lib/目录下,该目录大概率仅对Oracle账号开放访问权限。其他账号执行程序时,动态链接器无法加载这个依赖库,会直接导致Oracle ODBC库初始化失败,进而引发SQLHENV分配错误和后续的dlclose段错误。
- 检查
/home/oracle/oracle/lib/目录权限,确保其他账号拥有读和执行权限; - 要么给该目录添加其他用户的访问权限,要么将
libnnz19.so复制到系统公共库目录(如/usr/lib64/),或者在运行程序前设置LD_LIBRARY_PATH环境变量,包含该目录路径:export LD_LIBRARY_PATH=/home/oracle/oracle/lib:$LD_LIBRARY_PATH
2. Oracle核心环境变量配置
Oracle安装账号通常会配置ORACLE_HOME、ORACLE_BASE等核心环境变量,这些是Oracle库正常初始化的必要条件。其他账号缺少这些变量时,库的运行逻辑会出现异常:
- 运行程序前手动设置环境变量:
export ORACLE_HOME=/home/oracle/oracle export LD_LIBRARY_PATH=$ORACLE_HOME/lib:$LD_LIBRARY_PATH - 或者将这些变量配置到其他账号的
.bashrc或.profile文件中,实现永久生效。
3. ODBC配置文件的访问权限
ODBC运行依赖odbcinst.ini和odbc.ini配置文件,通常位于/etc/目录或用户家目录下。如果其他账号没有读取这些配置文件的权限,会导致ODBC驱动初始化失败:
- 检查
/etc/odbcinst.ini和/etc/odbc.ini的权限,确保其他账号拥有读权限; - 如果是用户级配置,确保其他账号家目录下存在正确的配置文件,或直接使用系统级配置。
4. 程序代码中的潜在问题
你的代码存在两处明显的逻辑问题,虽然Oracle账号下运行正常,但可能在其他环境中放大错误:
- 注释了
SQLAllocConnect的定义和赋值,但在API检查逻辑中依然保留了!SQLAllocConnect的判断,这会导致未初始化变量的非法判断,建议要么删除该判断,要么取消注释相关定义与赋值; - 判断SQLRETURN返回值时使用了
sReturn < 0,但ODBC的返回值包含SQL_SUCCESS_WITH_INFO(正数)这类非错误状态,正确的做法是使用SQL_SUCCEEDED()或SQL_FAILED()宏来判断:if (SQL_FAILED(sReturn)) { // 错误处理逻辑 }
5. 动态加载参数优化
当前使用RTLD_NOW参数加载库,会立即解析所有符号,但如果依赖库加载不完整,可能导致后续API调用异常。建议添加RTLD_GLOBAL参数,确保库中的符号能被其他依赖库正常引用:
void* oracleLib = dlopen("libsqora.so.19.1", RTLD_NOW | RTLD_GLOBAL);
内容的提问来源于stack exchange,提问作者Kwang Hun Lee
相关产品推荐
相关产品推荐

